]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #89321 - tmiasko:rebase-resume-arg, r=estebank
authorManish Goregaokar <manishsmail@gmail.com>
Fri, 1 Oct 2021 06:41:07 +0000 (23:41 -0700)
committerGitHub <noreply@github.com>
Fri, 1 Oct 2021 06:41:07 +0000 (23:41 -0700)
Rebase resume argument projections during state transform

When remapping a resume argument with projections rebase them on top of
the new base.

The case where resume argument has projections is unusual, but might
arise with box syntax where the assignment is performed directly into
the box without an intermediate temporary.

Fixes #85635.

647 files changed:
Cargo.lock
Cargo.toml
compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
compiler/rustc_borrowck/src/lib.rs
compiler/rustc_borrowck/src/nll.rs
compiler/rustc_borrowck/src/region_infer/mod.rs
compiler/rustc_borrowck/src/renumber.rs
compiler/rustc_borrowck/src/type_check/canonical.rs
compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
compiler/rustc_borrowck/src/type_check/input_output.rs
compiler/rustc_borrowck/src/type_check/mod.rs
compiler/rustc_borrowck/src/type_check/relate_tys.rs
compiler/rustc_codegen_cranelift/scripts/filter_profile.rs
compiler/rustc_codegen_cranelift/src/driver/aot.rs
compiler/rustc_codegen_cranelift/src/lib.rs
compiler/rustc_codegen_cranelift/src/metadata.rs
compiler/rustc_codegen_gcc/.github/workflows/main.yml [new file with mode: 0644]
compiler/rustc_codegen_gcc/.gitignore [new file with mode: 0644]
compiler/rustc_codegen_gcc/Cargo.lock [new file with mode: 0644]
compiler/rustc_codegen_gcc/Cargo.toml [new file with mode: 0644]
compiler/rustc_codegen_gcc/LICENSE-APACHE [new file with mode: 0644]
compiler/rustc_codegen_gcc/LICENSE-MIT [new file with mode: 0644]
compiler/rustc_codegen_gcc/Readme.md [new file with mode: 0644]
compiler/rustc_codegen_gcc/build.sh [new file with mode: 0755]
compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml [new file with mode: 0644]
compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh [new file with mode: 0755]
compiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh [new file with mode: 0755]
compiler/rustc_codegen_gcc/build_sysroot/src/lib.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/cargo.sh [new file with mode: 0755]
compiler/rustc_codegen_gcc/clean_all.sh [new file with mode: 0755]
compiler/rustc_codegen_gcc/config.sh [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/alloc_example.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/alloc_system.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/dst-field-align.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/example.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/mini_core.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/mod_bench.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/std_example.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/subslice-patterns-const-eval.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/example/track-caller-attribute.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch [new file with mode: 0644]
compiler/rustc_codegen_gcc/patches/0023-core-Ignore-failing-tests.patch [new file with mode: 0644]
compiler/rustc_codegen_gcc/prepare.sh [new file with mode: 0755]
compiler/rustc_codegen_gcc/prepare_build.sh [new file with mode: 0755]
compiler/rustc_codegen_gcc/rust-toolchain [new file with mode: 0644]
compiler/rustc_codegen_gcc/rustup.sh [new file with mode: 0755]
compiler/rustc_codegen_gcc/src/abi.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/allocator.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/archive.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/asm.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/back/mod.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/back/write.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/base.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/builder.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/callee.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/common.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/consts.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/context.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/coverageinfo.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/debuginfo.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/declare.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/intrinsic/mod.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/intrinsic/simd.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/lib.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/mono_item.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/type_.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/src/type_of.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/test.sh [new file with mode: 0755]
compiler/rustc_codegen_gcc/tests/lib.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/abort1.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/abort2.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/array.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/asm.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/assign.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/closure.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/condition.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/empty_main.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/exit.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/exit_code.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/int_overflow.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/mut_ref.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/operations.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/return-tuple.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/slice.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/static.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/structs.rs [new file with mode: 0644]
compiler/rustc_codegen_gcc/tests/run/tuple.rs [new file with mode: 0644]
compiler/rustc_codegen_llvm/src/base.rs
compiler/rustc_codegen_llvm/src/lib.rs
compiler/rustc_codegen_ssa/Cargo.toml
compiler/rustc_codegen_ssa/src/back/link.rs
compiler/rustc_codegen_ssa/src/back/write.rs
compiler/rustc_codegen_ssa/src/base.rs
compiler/rustc_codegen_ssa/src/lib.rs
compiler/rustc_codegen_ssa/src/traits/backend.rs
compiler/rustc_const_eval/src/interpret/step.rs
compiler/rustc_data_structures/src/graph/scc/mod.rs
compiler/rustc_expand/src/expand.rs
compiler/rustc_hir/src/lang_items.rs
compiler/rustc_infer/src/infer/at.rs
compiler/rustc_infer/src/infer/canonical/query_response.rs
compiler/rustc_infer/src/infer/fudge.rs
compiler/rustc_infer/src/infer/higher_ranked/mod.rs
compiler/rustc_infer/src/infer/mod.rs
compiler/rustc_infer/src/infer/nll_relate/mod.rs
compiler/rustc_infer/src/infer/region_constraints/mod.rs
compiler/rustc_interface/src/passes.rs
compiler/rustc_interface/src/queries.rs
compiler/rustc_lint/src/context.rs
compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
compiler/rustc_metadata/src/lib.rs
compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
compiler/rustc_metadata/src/rmeta/encoder.rs
compiler/rustc_metadata/src/rmeta/mod.rs
compiler/rustc_middle/src/middle/cstore.rs
compiler/rustc_middle/src/ty/context.rs
compiler/rustc_middle/src/ty/fold.rs
compiler/rustc_middle/src/ty/print/mod.rs
compiler/rustc_middle/src/ty/print/pretty.rs
compiler/rustc_middle/src/ty/util.rs
compiler/rustc_mir_build/src/build/expr/into.rs
compiler/rustc_mir_build/src/thir/cx/expr.rs
compiler/rustc_mir_build/src/thir/cx/mod.rs
compiler/rustc_mir_build/src/thir/pattern/check_match.rs
compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
compiler/rustc_mir_dataflow/src/impls/mod.rs
compiler/rustc_resolve/src/diagnostics.rs
compiler/rustc_resolve/src/late.rs
compiler/rustc_resolve/src/late/diagnostics.rs
compiler/rustc_resolve/src/late/lifetimes.rs
compiler/rustc_resolve/src/lib.rs
compiler/rustc_session/src/session.rs
compiler/rustc_target/src/spec/aarch64_kmc_solid_asp3.rs [new file with mode: 0644]
compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabi.rs [new file with mode: 0644]
compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabihf.rs [new file with mode: 0644]
compiler/rustc_target/src/spec/mod.rs
compiler/rustc_target/src/spec/solid_base.rs [new file with mode: 0644]
compiler/rustc_trait_selection/src/autoderef.rs
compiler/rustc_trait_selection/src/opaque_types.rs
compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
compiler/rustc_trait_selection/src/traits/select/confirmation.rs
compiler/rustc_trait_selection/src/traits/select/mod.rs
compiler/rustc_typeck/src/astconv/generics.rs
compiler/rustc_typeck/src/check/callee.rs
compiler/rustc_typeck/src/check/check.rs
compiler/rustc_typeck/src/check/closure.rs
compiler/rustc_typeck/src/check/coercion.rs
compiler/rustc_typeck/src/check/demand.rs
compiler/rustc_typeck/src/check/expr.rs
compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
compiler/rustc_typeck/src/check/method/suggest.rs
compiler/rustc_typeck/src/check/place_op.rs
compiler/rustc_typeck/src/check/upvar.rs
compiler/rustc_typeck/src/check/writeback.rs
compiler/rustc_typeck/src/expr_use_visitor.rs
compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
library/core/src/cell.rs
library/core/src/convert/mod.rs
library/core/src/iter/range.rs
library/core/src/lib.rs
library/core/src/option.rs
library/core/src/result.rs
library/core/src/slice/sort.rs
library/core/tests/convert.rs [new file with mode: 0644]
library/core/tests/lib.rs
library/panic_abort/src/lib.rs
library/panic_unwind/src/lib.rs
library/std/build.rs
library/std/src/ffi/c_str.rs
library/std/src/lib.rs
library/std/src/os/mod.rs
library/std/src/os/solid/ffi.rs [new file with mode: 0644]
library/std/src/os/solid/io.rs [new file with mode: 0644]
library/std/src/os/solid/mod.rs [new file with mode: 0644]
library/std/src/process.rs
library/std/src/rt.rs
library/std/src/sync/mutex.rs
library/std/src/sync/rwlock.rs
library/std/src/sys/itron/abi.rs [new file with mode: 0644]
library/std/src/sys/itron/condvar.rs [new file with mode: 0644]
library/std/src/sys/itron/error.rs [new file with mode: 0644]
library/std/src/sys/itron/mutex.rs [new file with mode: 0644]
library/std/src/sys/itron/spin.rs [new file with mode: 0644]
library/std/src/sys/itron/task.rs [new file with mode: 0644]
library/std/src/sys/itron/thread.rs [new file with mode: 0644]
library/std/src/sys/itron/time.rs [new file with mode: 0644]
library/std/src/sys/itron/time/tests.rs [new file with mode: 0644]
library/std/src/sys/mod.rs
library/std/src/sys/solid/abi/fs.rs [new file with mode: 0644]
library/std/src/sys/solid/abi/mod.rs [new file with mode: 0644]
library/std/src/sys/solid/abi/sockets.rs [new file with mode: 0644]
library/std/src/sys/solid/alloc.rs [new file with mode: 0644]
library/std/src/sys/solid/env.rs [new file with mode: 0644]
library/std/src/sys/solid/error.rs [new file with mode: 0644]
library/std/src/sys/solid/fs.rs [new file with mode: 0644]
library/std/src/sys/solid/io.rs [new file with mode: 0644]
library/std/src/sys/solid/memchr.rs [new file with mode: 0644]
library/std/src/sys/solid/mod.rs [new file with mode: 0644]
library/std/src/sys/solid/net.rs [new file with mode: 0644]
library/std/src/sys/solid/os.rs [new file with mode: 0644]
library/std/src/sys/solid/path.rs [new file with mode: 0644]
library/std/src/sys/solid/rwlock.rs [new file with mode: 0644]
library/std/src/sys/solid/stdio.rs [new file with mode: 0644]
library/std/src/sys/solid/thread_local_dtor.rs [new file with mode: 0644]
library/std/src/sys/solid/thread_local_key.rs [new file with mode: 0644]
library/std/src/sys/solid/time.rs [new file with mode: 0644]
library/std/src/sys/unix/mod.rs
library/std/src/sys/unix/thread.rs
library/std/src/sys_common/mod.rs
library/std/src/sys_common/rt.rs [deleted file]
library/std/src/sys_common/thread_info.rs
library/std/src/thread/mod.rs
library/std/src/time.rs
library/test/src/formatters/junit.rs
library/unwind/src/lib.rs
rustfmt.toml
src/bootstrap/check.rs
src/bootstrap/defaults/config.library.toml
src/bootstrap/setup.rs
src/ci/scripts/install-clang.sh
src/doc/book
src/doc/nomicon
src/doc/reference
src/doc/rust-by-example
src/doc/rustc-dev-guide
src/doc/rustc/src/SUMMARY.md
src/doc/rustc/src/platform-support.md
src/doc/rustc/src/platform-support/kmc-solid.md [new file with mode: 0644]
src/librustdoc/clean/mod.rs
src/librustdoc/clean/simplify.rs
src/librustdoc/clean/types.rs
src/librustdoc/core.rs
src/librustdoc/html/format.rs
src/librustdoc/html/highlight.rs
src/librustdoc/html/highlight/fixtures/union.html [new file with mode: 0644]
src/librustdoc/html/highlight/fixtures/union.rs [new file with mode: 0644]
src/librustdoc/html/highlight/tests.rs
src/librustdoc/html/render/cache.rs
src/librustdoc/json/conversions.rs
src/librustdoc/json/mod.rs
src/librustdoc/lib.rs
src/test/mir-opt/const_promotion_extern_static.BAR-promoted[0].SimplifyCfg-elaborate-drops.after.mir
src/test/mir-opt/const_promotion_extern_static.BAR.PromoteTemps.diff
src/test/mir-opt/const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-elaborate-drops.after.mir
src/test/mir-opt/const_promotion_extern_static.FOO.PromoteTemps.diff
src/test/mir-opt/deduplicate_blocks.is_line_doc_comment_2.DeduplicateBlocks.diff
src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff
src/test/mir-opt/inline/inline_shims.clone.Inline.diff
src/test/mir-opt/inline/inline_trait_method.test.Inline.after.mir
src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.a.Inline.after.mir
src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir
src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.c.Inline.after.mir
src/test/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir
src/test/mir-opt/lower_slice_len.bound.LowerSliceLenCalls.diff
src/test/mir-opt/no_spurious_drop_after_call.main.ElaborateDrops.before.mir
src/test/mir-opt/receiver_ptr_mutability.main.mir_map.0.mir
src/test/mir-opt/retag.main.SimplifyCfg-elaborate-drops.after.mir
src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs
src/test/run-make-fulldeps/target-specs/foo.rs
src/test/rustdoc-json/unions/impl.rs [new file with mode: 0644]
src/test/ui/array-slice-vec/vec-mut-iter-borrow.stderr
src/test/ui/asm/issue-89305.rs [new file with mode: 0644]
src/test/ui/asm/issue-89305.stderr [new file with mode: 0644]
src/test/ui/associated-consts/associated-const-ambiguity-report.stderr
src/test/ui/associated-types/hr-associated-type-bound-2.stderr
src/test/ui/async-await/issue-61452.stderr
src/test/ui/async-await/issues/issue-61187.stderr
src/test/ui/autoref-autoderef/issue-38940.stderr
src/test/ui/binop/binop-move-semantics.stderr
src/test/ui/borrowck/borrow-tuple-fields.stderr
src/test/ui/borrowck/borrowck-argument.stderr
src/test/ui/borrowck/borrowck-auto-mut-ref-to-immut-var.stderr
src/test/ui/borrowck/borrowck-borrow-immut-deref-of-box-as-mut.stderr
src/test/ui/borrowck/borrowck-borrow-mut-object-twice.stderr
src/test/ui/borrowck/borrowck-borrow-overloaded-auto-deref.stderr
src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr
src/test/ui/borrowck/borrowck-closures-unique-imm.stderr
src/test/ui/borrowck/borrowck-describe-lvalue.stderr
src/test/ui/borrowck/borrowck-for-loop-head-linkage.nll.stderr [deleted file]
src/test/ui/borrowck/borrowck-insert-during-each.stderr
src/test/ui/borrowck/borrowck-issue-2657-1.stderr
src/test/ui/borrowck/borrowck-lend-flow-if.stderr
src/test/ui/borrowck/borrowck-lend-flow.stderr
src/test/ui/borrowck/borrowck-loan-blocks-move-cc.stderr
src/test/ui/borrowck/borrowck-loan-blocks-move.stderr
src/test/ui/borrowck/borrowck-loan-in-overloaded-op.stderr
src/test/ui/borrowck/borrowck-loan-rcvr-overloaded-op.stderr
src/test/ui/borrowck/borrowck-loan-rcvr.stderr
src/test/ui/borrowck/borrowck-move-from-subpath-of-borrowed-path.stderr
src/test/ui/borrowck/borrowck-move-mut-base-ptr.stderr
src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr
src/test/ui/borrowck/borrowck-mut-borrow-linear-errors.stderr
src/test/ui/borrowck/borrowck-mut-borrow-of-mut-base-ptr.stderr
src/test/ui/borrowck/borrowck-object-lifetime.nll.stderr [deleted file]
src/test/ui/borrowck/borrowck-object-lifetime.stderr
src/test/ui/borrowck/borrowck-overloaded-index-autoderef.stderr
src/test/ui/borrowck/borrowck-report-with-custom-diagnostic.stderr
src/test/ui/borrowck/borrowck-swap-mut-base-ptr.stderr
src/test/ui/borrowck/borrowck-union-borrow-nested.stderr
src/test/ui/borrowck/borrowck-uniq-via-lend.stderr
src/test/ui/borrowck/borrowck-vec-pattern-loan-from-mut.stderr
src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr
src/test/ui/borrowck/index-mut-help-with-impl.stderr
src/test/ui/borrowck/index-mut-help.stderr
src/test/ui/borrowck/issue-42344.stderr
src/test/ui/borrowck/issue-51117.stderr
src/test/ui/borrowck/issue-81365-10.stderr
src/test/ui/borrowck/issue-81365-5.stderr
src/test/ui/borrowck/issue-82032.stderr
src/test/ui/borrowck/issue-82462.nll.stderr [deleted file]
src/test/ui/borrowck/issue-85581.stderr
src/test/ui/borrowck/issue-85765.stderr
src/test/ui/borrowck/mut-borrow-of-mut-ref.stderr
src/test/ui/borrowck/mut-borrow-outside-loop.stderr
src/test/ui/borrowck/two-phase-across-loop.stderr
src/test/ui/borrowck/two-phase-cannot-nest-mut-self-calls.stderr
src/test/ui/borrowck/two-phase-multi-mut.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2015.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2018.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2015.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2018.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.nll.stderr
src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr
src/test/ui/borrowck/two-phase-sneaky.stderr
src/test/ui/borrowck/two-phase-surprise-no-conflict.stderr
src/test/ui/box/leak-alloc.stderr
src/test/ui/cannot-mutate-captured-non-mut-var.stderr
src/test/ui/closures/2229_closure_analysis/issue-88118-2.rs [new file with mode: 0644]
src/test/ui/closures/2229_closure_analysis/issue-88118-2.stderr [new file with mode: 0644]
src/test/ui/codemap_tests/issue-11715.stderr
src/test/ui/codemap_tests/one_line.stderr
src/test/ui/const-generics/issues/issue-67375.full.stderr
src/test/ui/consts/const_in_pattern/issue-78057.stderr
src/test/ui/consts/const_let_assign3.stderr
src/test/ui/consts/try-operator.rs [new file with mode: 0644]
src/test/ui/did_you_mean/issue-34126.stderr
src/test/ui/did_you_mean/issue-35937.stderr
src/test/ui/did_you_mean/issue-38147-1.stderr
src/test/ui/did_you_mean/issue-38147-2.stderr
src/test/ui/did_you_mean/issue-38147-3.stderr
src/test/ui/did_you_mean/issue-38147-4.stderr
src/test/ui/did_you_mean/issue-40823.stderr
src/test/ui/did_you_mean/recursion_limit.stderr
src/test/ui/did_you_mean/recursion_limit_deref.stderr
src/test/ui/did_you_mean/recursion_limit_macro.stderr
src/test/ui/dropck/drop-with-active-borrows-1.stderr
src/test/ui/dropck/drop-with-active-borrows-2.stderr
src/test/ui/error-codes/E0034.stderr
src/test/ui/error-codes/E0055.stderr
src/test/ui/error-codes/E0161.edition.stderr
src/test/ui/error-codes/E0161.migrate.stderr
src/test/ui/error-codes/E0161.nll.stderr
src/test/ui/error-codes/E0161.zflags.stderr
src/test/ui/error-codes/E0275.stderr
src/test/ui/error-codes/E0407.stderr
src/test/ui/error-codes/E0499.stderr
src/test/ui/error-codes/E0502.nll.stderr
src/test/ui/error-codes/E0502.stderr
src/test/ui/error-codes/E0503.stderr
src/test/ui/error-codes/E0505.stderr
src/test/ui/error-codes/E0507.stderr
src/test/ui/generator/dropck-resume.stderr
src/test/ui/generator/dropck.stderr
src/test/ui/hashmap/hashmap-iter-value-lifetime.nll.stderr [deleted file]
src/test/ui/hashmap/hashmap-iter-value-lifetime.stderr
src/test/ui/hashmap/hashmap-lifetimes.nll.stderr [deleted file]
src/test/ui/hashmap/hashmap-lifetimes.stderr
src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.nll.stderr
src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.nll.stderr
src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.nll.stderr
src/test/ui/hrtb/hrtb-debruijn-in-receiver.stderr
src/test/ui/hygiene/assoc_item_ctxt.stderr
src/test/ui/hygiene/fields-numeric-borrowck.stderr
src/test/ui/hygiene/globs.stderr
src/test/ui/imports/glob-resolve1.stderr
src/test/ui/imports/issue-4366-2.stderr
src/test/ui/infinite/infinite-autoderef.stderr
src/test/ui/infinite/infinite-macro-expansion.stderr
src/test/ui/issues/issue-13497-2.stderr
src/test/ui/issues/issue-16098.stderr
src/test/ui/issues/issue-18400.stderr
src/test/ui/issues/issue-19163.stderr
src/test/ui/issues/issue-20413.stderr
src/test/ui/issues/issue-21600.stderr
src/test/ui/issues/issue-23122-2.stderr
src/test/ui/issues/issue-41726.stderr
src/test/ui/issues/issue-42106.stderr
src/test/ui/issues/issue-44405.stderr
src/test/ui/issues/issue-47646.stderr
src/test/ui/issues/issue-52126-assign-op-invariance.stderr
src/test/ui/issues/issue-61108.stderr
src/test/ui/issues/issue-81584.stderr
src/test/ui/lang-items/lang-item-generic-requirements.rs
src/test/ui/lang-items/lang-item-generic-requirements.stderr
src/test/ui/lifetimes/borrowck-let-suggestion.stderr
src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
src/test/ui/lint/must_not_suspend/mutex.rs [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/mutex.stderr [new file with mode: 0644]
src/test/ui/lint/unaligned_references.stderr
src/test/ui/lub-glb/old-lub-glb-hr-noteq1.nll.stderr
src/test/ui/lub-glb/old-lub-glb-object.nll.stderr
src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.rs
src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.stderr
src/test/ui/macros/trace_faulty_macros.stderr
src/test/ui/match/issue-74050-end-span.stderr
src/test/ui/methods/method-ambig-two-traits-from-impls2.stderr
src/test/ui/methods/method-self-arg-2.stderr
src/test/ui/moves/issue-72649-uninit-in-loop.rs [new file with mode: 0644]
src/test/ui/moves/issue-72649-uninit-in-loop.stderr [new file with mode: 0644]
src/test/ui/moves/move-fn-self-receiver.stderr
src/test/ui/mut/mut-cant-alias.stderr
src/test/ui/mut/mut-suggestion.stderr
src/test/ui/nll/closure-access-spans.stderr
src/test/ui/nll/closure-borrow-spans.stderr
src/test/ui/nll/get_default.nll.stderr [deleted file]
src/test/ui/nll/get_default.stderr
src/test/ui/nll/issue-46589.stderr
src/test/ui/nll/issue-51191.stderr
src/test/ui/nll/issue-52669.stderr
src/test/ui/nll/issue-53773.stderr
src/test/ui/nll/issue-54556-niconii.stderr
src/test/ui/nll/issue-62007-assign-const-index.stderr
src/test/ui/nll/issue-62007-assign-differing-fields.stderr
src/test/ui/nll/loan_ends_mid_block_vec.stderr
src/test/ui/nll/polonius/assignment-to-differing-field.stderr
src/test/ui/nll/region-ends-after-if-condition.nll.stderr [deleted file]
src/test/ui/nll/relate_tys/fn-subtype.stderr
src/test/ui/nll/relate_tys/hr-fn-aaa-as-aba.stderr
src/test/ui/nll/relate_tys/trait-hrtb.stderr
src/test/ui/nll/return_from_loop.stderr
src/test/ui/object-safety/object-safety-by-value-self-use.stderr
src/test/ui/pattern/usefulness/const-private-fields.rs [new file with mode: 0644]
src/test/ui/pattern/usefulness/consts-opaque.stderr
src/test/ui/pattern/usefulness/integer-ranges/reachability.stderr
src/test/ui/pattern/usefulness/issue-3601.stderr
src/test/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs [new file with mode: 0644]
src/test/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr [new file with mode: 0644]
src/test/ui/recursion/issue-83150.stderr
src/test/ui/recursion_limit/no-value.rs [new file with mode: 0644]
src/test/ui/recursion_limit/no-value.stderr [new file with mode: 0644]
src/test/ui/recursion_limit/zero-overflow.rs [new file with mode: 0644]
src/test/ui/recursion_limit/zero-overflow.stderr [new file with mode: 0644]
src/test/ui/recursion_limit/zero.stderr
src/test/ui/regions/region-object-lifetime-5.rs
src/test/ui/regions/region-object-lifetime-5.stderr
src/test/ui/resolve/issue-42944.stderr
src/test/ui/resolve/issue-88472.rs [new file with mode: 0644]
src/test/ui/resolve/issue-88472.stderr [new file with mode: 0644]
src/test/ui/resolve/privacy-enum-ctor.stderr
src/test/ui/resolve/privacy-struct-ctor.stderr
src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.nll.stderr [deleted file]
src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr
src/test/ui/rfc1623.nll.stderr
src/test/ui/self/self_type_keyword.stderr
src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.stderr
src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr
src/test/ui/span/borrowck-call-method-from-mut-aliasable.stderr
src/test/ui/span/borrowck-fn-in-const-b.stderr
src/test/ui/span/borrowck-let-suggestion-suffixes.stderr
src/test/ui/span/borrowck-object-mutability.stderr
src/test/ui/span/destructor-restrictions.stderr
src/test/ui/span/issue-23338-locals-die-before-temps-of-body.stderr
src/test/ui/span/issue-36537.stderr
src/test/ui/span/issue-40157.stderr
src/test/ui/span/issue-7575.stderr
src/test/ui/span/mut-arg-hint.stderr
src/test/ui/span/mut-ptr-cant-outlive-ref.stderr
src/test/ui/span/regionck-unboxed-closure-lifetimes.stderr
src/test/ui/span/regions-escape-loop-via-vec.stderr
src/test/ui/span/send-is-not-static-std-sync.stderr
src/test/ui/span/slice-borrow.stderr
src/test/ui/suggestions/core-std-import-order-issue-83564.rs [new file with mode: 0644]
src/test/ui/suggestions/core-std-import-order-issue-83564.stderr [new file with mode: 0644]
src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.nll.stderr
src/test/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.stderr
src/test/ui/suggestions/issue-88730.rs [new file with mode: 0644]
src/test/ui/suggestions/issue-88730.stderr [new file with mode: 0644]
src/test/ui/suggestions/negative-literal-index.fixed [new file with mode: 0644]
src/test/ui/suggestions/negative-literal-index.rs [new file with mode: 0644]
src/test/ui/suggestions/negative-literal-index.stderr [new file with mode: 0644]
src/test/ui/suggestions/suggest-trait-items.rs [new file with mode: 0644]
src/test/ui/suggestions/suggest-trait-items.stderr [new file with mode: 0644]
src/test/ui/traits/mutual-recursion-issue-75860.stderr
src/test/ui/typeck/call-block.rs [new file with mode: 0644]
src/test/ui/typeck/call-block.stderr [new file with mode: 0644]
src/test/ui/unop-move-semantics.stderr
src/test/ui/use/use-after-move-implicity-coerced-object.stderr
src/tools/cargo
src/tools/clippy/.cargo/config
src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md
src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md
src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
src/tools/clippy/CHANGELOG.md
src/tools/clippy/Cargo.toml
src/tools/clippy/README.md
src/tools/clippy/clippy_dev/Cargo.toml
src/tools/clippy/clippy_dev/src/bless.rs
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/derivable_impls.rs
src/tools/clippy/clippy_lints/src/disallowed_method.rs
src/tools/clippy/clippy_lints/src/disallowed_type.rs
src/tools/clippy/clippy_lints/src/escape.rs
src/tools/clippy/clippy_lints/src/eta_reduction.rs
src/tools/clippy/clippy_lints/src/float_literal.rs
src/tools/clippy/clippy_lints/src/format.rs
src/tools/clippy/clippy_lints/src/formatting.rs
src/tools/clippy/clippy_lints/src/if_let_some_result.rs [deleted file]
src/tools/clippy/clippy_lints/src/if_then_panic.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs
src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
src/tools/clippy/clippy_lints/src/macro_use.rs
src/tools/clippy/clippy_lints/src/match_result_ok.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/matches.rs
src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/misc.rs
src/tools/clippy/clippy_lints/src/mut_key.rs
src/tools/clippy/clippy_lints/src/needless_borrow.rs
src/tools/clippy/clippy_lints/src/non_expressive_names.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/regex.rs
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/same_name_method.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/types/box_collection.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/types/box_vec.rs [deleted file]
src/tools/clippy/clippy_lints/src/types/mod.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/ast_utils.rs
src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
src/tools/clippy/clippy_utils/src/higher.rs
src/tools/clippy/clippy_utils/src/hir_utils.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/src/main.rs
src/tools/clippy/tests/cargo/mod.rs
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/integration.rs
src/tools/clippy/tests/ui-internal/invalid_paths.stderr
src/tools/clippy/tests/ui-toml/toml_disallowed_method/clippy.toml
src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr
src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.rs
src/tools/clippy/tests/ui-toml/toml_trivially_copy/test.stderr
src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/box_collection.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/box_collection.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/box_vec.rs [deleted file]
src/tools/clippy/tests/ui/box_vec.stderr [deleted file]
src/tools/clippy/tests/ui/def_id_nocore.rs
src/tools/clippy/tests/ui/def_id_nocore.stderr
src/tools/clippy/tests/ui/default_trait_access.fixed
src/tools/clippy/tests/ui/default_trait_access.rs
src/tools/clippy/tests/ui/deref_addrof.fixed
src/tools/clippy/tests/ui/deref_addrof.rs
src/tools/clippy/tests/ui/derivable_impls.rs
src/tools/clippy/tests/ui/eq_op.rs
src/tools/clippy/tests/ui/eta.fixed
src/tools/clippy/tests/ui/eta.rs
src/tools/clippy/tests/ui/eta.stderr
src/tools/clippy/tests/ui/eval_order_dependence.rs
src/tools/clippy/tests/ui/eval_order_dependence.stderr
src/tools/clippy/tests/ui/excessive_precision.fixed
src/tools/clippy/tests/ui/excessive_precision.stderr
src/tools/clippy/tests/ui/explicit_deref_methods.fixed
src/tools/clippy/tests/ui/explicit_deref_methods.rs
src/tools/clippy/tests/ui/fallible_impl_from.rs
src/tools/clippy/tests/ui/fallible_impl_from.stderr
src/tools/clippy/tests/ui/float_cmp.rs
src/tools/clippy/tests/ui/float_cmp.stderr
src/tools/clippy/tests/ui/for_loop_fixable.fixed
src/tools/clippy/tests/ui/for_loop_fixable.rs
src/tools/clippy/tests/ui/if_let_some_result.fixed [deleted file]
src/tools/clippy/tests/ui/if_let_some_result.rs [deleted file]
src/tools/clippy/tests/ui/if_let_some_result.stderr [deleted file]
src/tools/clippy/tests/ui/if_then_panic.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/if_then_panic.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/if_then_panic.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/infinite_loop.rs
src/tools/clippy/tests/ui/infinite_loop.stderr
src/tools/clippy/tests/ui/inherent_to_string.rs
src/tools/clippy/tests/ui/inherent_to_string.stderr
src/tools/clippy/tests/ui/iter_not_returning_iterator.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/logic_bug.rs
src/tools/clippy/tests/ui/manual_split_once.fixed
src/tools/clippy/tests/ui/manual_split_once.rs
src/tools/clippy/tests/ui/manual_split_once.stderr
src/tools/clippy/tests/ui/many_single_char_names.rs
src/tools/clippy/tests/ui/map_flatten.fixed
src/tools/clippy/tests/ui/map_flatten.rs
src/tools/clippy/tests/ui/map_flatten.stderr
src/tools/clippy/tests/ui/match_result_ok.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/match_result_ok.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/match_result_ok.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/match_single_binding.fixed
src/tools/clippy/tests/ui/match_single_binding.rs
src/tools/clippy/tests/ui/mut_key.rs
src/tools/clippy/tests/ui/mut_key.stderr
src/tools/clippy/tests/ui/needless_borrow.fixed
src/tools/clippy/tests/ui/needless_borrow.rs
src/tools/clippy/tests/ui/needless_borrow.stderr
src/tools/clippy/tests/ui/needless_pass_by_value.rs
src/tools/clippy/tests/ui/needless_pass_by_value.stderr
src/tools/clippy/tests/ui/needless_return.fixed
src/tools/clippy/tests/ui/needless_return.rs
src/tools/clippy/tests/ui/needless_return.stderr
src/tools/clippy/tests/ui/nonminimal_bool.rs
src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
src/tools/clippy/tests/ui/op_ref.rs
src/tools/clippy/tests/ui/op_ref.stderr
src/tools/clippy/tests/ui/overflow_check_conditional.rs
src/tools/clippy/tests/ui/overflow_check_conditional.stderr
src/tools/clippy/tests/ui/ptr_arg.rs
src/tools/clippy/tests/ui/ptr_arg.stderr
src/tools/clippy/tests/ui/repeat_once.fixed
src/tools/clippy/tests/ui/repeat_once.rs
src/tools/clippy/tests/ui/same_name_method.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/same_name_method.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs
src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr
src/tools/clippy/tests/ui/suspicious_else_formatting.rs
src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs
src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr
src/tools/clippy/tests/ui/while_let_on_iterator.fixed
src/tools/clippy/tests/ui/while_let_on_iterator.stderr
src/tools/clippy/tests/ui/wrong_self_convention2.rs
src/tools/rust-analyzer
src/tools/tidy/src/lib.rs

index 6b3a714920bb4938694beec3018b8f4ade2964ae..157c1749c8782ee3c93bc90494062fe78b74e064 100644 (file)
@@ -3706,6 +3706,7 @@ dependencies = [
  "rustc_incremental",
  "rustc_index",
  "rustc_macros",
+ "rustc_metadata",
  "rustc_middle",
  "rustc_serialize",
  "rustc_session",
index 3822da2ccd5e4694756480493eb2ff55afcce26e..ce7073886c20e95658065773511c7a05174d5274 100644 (file)
@@ -41,6 +41,7 @@ members = [
 exclude = [
   "build",
   "compiler/rustc_codegen_cranelift",
+  "compiler/rustc_codegen_gcc",
   "src/test/rustdoc-gui",
   # HACK(eddyb) This hardcodes the fact that our CI uses `/checkout/obj`.
   "obj",
index 366ade1a71388940210cec5d8f6a3d699dfb8bcf..15309ccd8df278ef157210533c15436bd75780ad 100644 (file)
@@ -327,6 +327,7 @@ fn nice_error(
     }
 }
 
+#[instrument(skip(fulfill_cx, infcx), level = "debug")]
 fn try_extract_error_from_fulfill_cx<'tcx>(
     mut fulfill_cx: Box<dyn TraitEngine<'tcx> + 'tcx>,
     infcx: &InferCtxt<'_, 'tcx>,
@@ -341,7 +342,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
     let _errors = fulfill_cx.select_all_or_error(infcx).err().unwrap_or_else(Vec::new);
 
     let (sub_region, cause) = infcx.with_region_constraints(|region_constraints| {
-        debug!(?region_constraints);
+        debug!("{:#?}", region_constraints);
         region_constraints.constraints.iter().find_map(|(constraint, cause)| {
             match *constraint {
                 Constraint::RegSubReg(sub, sup) if sup == placeholder_region && sup != sub => {
@@ -356,7 +357,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
         })
     })?;
 
-    debug!(?sub_region, ?cause);
+    debug!(?sub_region, "cause = {:#?}", cause);
     let nice_error = match (error_region, sub_region) {
         (Some(error_region), &ty::ReVar(vid)) => NiceRegionError::new(
             infcx,
index f4cbbb60b053a2625f1c4578c96545d09a04a53c..37398894a202bf5829ba048de43326427bf4784f 100644 (file)
@@ -10,8 +10,7 @@
     ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
 };
 use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
-use rustc_mir_dataflow::drop_flag_effects;
-use rustc_mir_dataflow::move_paths::{MoveOutIndex, MovePathIndex};
+use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
 use rustc_span::source_map::DesugaringKind;
 use rustc_span::symbol::sym;
 use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
@@ -1531,25 +1530,45 @@ fn predecessor_locations(
             }
         }
 
+        let mut mpis = vec![mpi];
+        let move_paths = &self.move_data.move_paths;
+        mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
+
         let mut stack = Vec::new();
-        stack.extend(predecessor_locations(self.body, location).map(|predecessor| {
-            let is_back_edge = location.dominates(predecessor, &self.dominators);
-            (predecessor, is_back_edge)
-        }));
+        let mut back_edge_stack = Vec::new();
+
+        predecessor_locations(self.body, location).for_each(|predecessor| {
+            if location.dominates(predecessor, &self.dominators) {
+                back_edge_stack.push(predecessor)
+            } else {
+                stack.push(predecessor);
+            }
+        });
+
+        let mut reached_start = false;
+
+        /* Check if the mpi is initialized as an argument */
+        let mut is_argument = false;
+        for arg in self.body.args_iter() {
+            let path = self.move_data.rev_lookup.find_local(arg);
+            if mpis.contains(&path) {
+                is_argument = true;
+            }
+        }
 
         let mut visited = FxHashSet::default();
         let mut move_locations = FxHashSet::default();
         let mut reinits = vec![];
         let mut result = vec![];
 
-        'dfs: while let Some((location, is_back_edge)) = stack.pop() {
+        let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
             debug!(
                 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
                 location, is_back_edge
             );
 
             if !visited.insert(location) {
-                continue;
+                return true;
             }
 
             // check for moves
@@ -1568,10 +1587,6 @@ fn predecessor_locations(
                 // worry about the other case: that is, if there is a move of a.b.c, it is already
                 // marked as a move of a.b and a as well, so we will generate the correct errors
                 // there.
-                let mut mpis = vec![mpi];
-                let move_paths = &self.move_data.move_paths;
-                mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
-
                 for moi in &self.move_data.loc_map[location] {
                     debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
                     let path = self.move_data.moves[*moi].path;
@@ -1599,33 +1614,70 @@ fn predecessor_locations(
                         // Because we stop the DFS here, we only highlight `let c = a`,
                         // and not `let b = a`. We will of course also report an error at
                         // `let c = a` which highlights `let b = a` as the move.
-                        continue 'dfs;
+                        return true;
                     }
                 }
             }
 
             // check for inits
             let mut any_match = false;
-            drop_flag_effects::for_location_inits(
-                self.infcx.tcx,
-                &self.body,
-                self.move_data,
-                location,
-                |m| {
-                    if m == mpi {
-                        any_match = true;
+            for ii in &self.move_data.init_loc_map[location] {
+                let init = self.move_data.inits[*ii];
+                match init.kind {
+                    InitKind::Deep | InitKind::NonPanicPathOnly => {
+                        if mpis.contains(&init.path) {
+                            any_match = true;
+                        }
                     }
-                },
-            );
+                    InitKind::Shallow => {
+                        if mpi == init.path {
+                            any_match = true;
+                        }
+                    }
+                }
+            }
             if any_match {
                 reinits.push(location);
-                continue 'dfs;
+                return true;
             }
+            return false;
+        };
 
-            stack.extend(predecessor_locations(self.body, location).map(|predecessor| {
-                let back_edge = location.dominates(predecessor, &self.dominators);
-                (predecessor, is_back_edge || back_edge)
-            }));
+        while let Some(location) = stack.pop() {
+            if dfs_iter(&mut result, location, false) {
+                continue;
+            }
+
+            let mut has_predecessor = false;
+            predecessor_locations(self.body, location).for_each(|predecessor| {
+                if location.dominates(predecessor, &self.dominators) {
+                    back_edge_stack.push(predecessor)
+                } else {
+                    stack.push(predecessor);
+                }
+                has_predecessor = true;
+            });
+
+            if !has_predecessor {
+                reached_start = true;
+            }
+        }
+        if (is_argument || !reached_start) && result.is_empty() {
+            /* Process back edges (moves in future loop iterations) only if
+               the move path is definitely initialized upon loop entry,
+               to avoid spurious "in previous iteration" errors.
+               During DFS, if there's a path from the error back to the start
+               of the function with no intervening init or move, then the
+               move path may be uninitialized at loop entry.
+            */
+            while let Some(location) = back_edge_stack.pop() {
+                if dfs_iter(&mut result, location, true) {
+                    continue;
+                }
+
+                predecessor_locations(self.body, location)
+                    .for_each(|predecessor| back_edge_stack.push(predecessor));
+            }
         }
 
         // Check if we can reach these reinits from a move location.
index 72f4907a09f9849e4cf86f3ab54afd8b901629f7..6ffa0095e4b74a36c9525f1f47c2056fa36904db 100644 (file)
@@ -144,6 +144,7 @@ fn mir_borrowck<'tcx>(
 /// If `return_body_with_facts` is true, then return the body with non-erased
 /// region ids on which the borrow checking was performed together with Polonius
 /// facts.
+#[instrument(skip(infcx, input_body, input_promoted), level = "debug")]
 fn do_mir_borrowck<'a, 'tcx>(
     infcx: &InferCtxt<'a, 'tcx>,
     input_body: &Body<'tcx>,
@@ -152,7 +153,7 @@ fn do_mir_borrowck<'a, 'tcx>(
 ) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
     let def = input_body.source.with_opt_param().as_local().unwrap();
 
-    debug!("do_mir_borrowck(def = {:?})", def);
+    debug!(?def);
 
     let tcx = infcx.tcx;
     let param_env = tcx.param_env(def.did);
index 477b049b07596cda7b1c7eed0399d82fdf7938cb..e5924f9d08478ff484bcf5feef030bb7a48419cc 100644 (file)
@@ -54,6 +54,7 @@
 /// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
 /// regions (e.g., region parameters) declared on the function. That set will need to be given to
 /// `compute_regions`.
+#[instrument(skip(infcx, param_env, body, promoted), level = "debug")]
 pub(crate) fn replace_regions_in_mir<'cx, 'tcx>(
     infcx: &InferCtxt<'cx, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -62,7 +63,7 @@ pub(crate) fn replace_regions_in_mir<'cx, 'tcx>(
 ) -> UniversalRegions<'tcx> {
     let def = body.source.with_opt_param().as_local().unwrap();
 
-    debug!("replace_regions_in_mir(def={:?})", def);
+    debug!(?def);
 
     // Compute named region information. This also renumbers the inputs/outputs.
     let universal_regions = UniversalRegions::new(infcx, def, param_env);
index 917d69a5c866446ac4fc7ac6046d40642341326e..65d6e3a4ae574875fda0078e78982af562f8457d 100644 (file)
@@ -552,6 +552,7 @@ pub(crate) fn applied_member_constraints(
     /// Performs region inference and report errors if we see any
     /// unsatisfiable constraints. If this is a closure, returns the
     /// region requirements to propagate to our creator, if any.
+    #[instrument(skip(self, infcx, body, polonius_output), level = "debug")]
     pub(super) fn solve(
         &mut self,
         infcx: &InferCtxt<'_, 'tcx>,
@@ -607,10 +608,9 @@ pub(super) fn solve(
     /// for each region variable until all the constraints are
     /// satisfied. Note that some values may grow **too** large to be
     /// feasible, but we check this later.
+    #[instrument(skip(self, _body), level = "debug")]
     fn propagate_constraints(&mut self, _body: &Body<'tcx>) {
-        debug!("propagate_constraints()");
-
-        debug!("propagate_constraints: constraints={:#?}", {
+        debug!("constraints={:#?}", {
             let mut constraints: Vec<_> = self.constraints.outlives().iter().collect();
             constraints.sort();
             constraints
@@ -637,12 +637,13 @@ fn propagate_constraints(&mut self, _body: &Body<'tcx>) {
     /// computed, by unioning the values of its successors.
     /// Assumes that all successors have been computed already
     /// (which is assured by iterating over SCCs in dependency order).
+    #[instrument(skip(self), level = "debug")]
     fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
         let constraint_sccs = self.constraint_sccs.clone();
 
         // Walk each SCC `B` such that `A: B`...
         for &scc_b in constraint_sccs.successors(scc_a) {
-            debug!("propagate_constraint_sccs: scc_a = {:?} scc_b = {:?}", scc_a, scc_b);
+            debug!(?scc_b);
 
             // ...and add elements from `B` into `A`. One complication
             // arises because of universes: If `B` contains something
@@ -663,11 +664,7 @@ fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
             self.apply_member_constraint(scc_a, m_c_i, member_constraints.choice_regions(m_c_i));
         }
 
-        debug!(
-            "propagate_constraint_sccs: scc_a = {:?} has value {:?}",
-            scc_a,
-            self.scc_values.region_value_str(scc_a),
-        );
+        debug!(value = ?self.scc_values.region_value_str(scc_a));
     }
 
     /// Invoked for each `R0 member of [R1..Rn]` constraint.
@@ -681,14 +678,13 @@ fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
     /// is considered a *lower bound*.  If possible, we will modify
     /// the constraint to set it equal to one of the option regions.
     /// If we make any changes, returns true, else false.
+    #[instrument(skip(self, member_constraint_index), level = "debug")]
     fn apply_member_constraint(
         &mut self,
         scc: ConstraintSccIndex,
         member_constraint_index: NllMemberConstraintIndex,
         choice_regions: &[ty::RegionVid],
     ) -> bool {
-        debug!("apply_member_constraint(scc={:?}, choice_regions={:#?})", scc, choice_regions,);
-
         // Create a mutable vector of the options. We'll try to winnow
         // them down.
         let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec();
@@ -714,7 +710,7 @@ fn apply_member_constraint(
                 .universal_regions_outlived_by(scc)
                 .all(|lb| self.universal_region_relations.outlives(o_r, lb))
         });
-        debug!("apply_member_constraint: after lb, choice_regions={:?}", choice_regions);
+        debug!(?choice_regions, "after lb");
 
         // Now find all the *upper bounds* -- that is, each UB is a
         // free region that must outlive the member region `R0` (`UB:
@@ -723,10 +719,10 @@ fn apply_member_constraint(
         let rev_scc_graph = self.reverse_scc_graph();
         let universal_region_relations = &self.universal_region_relations;
         for ub in rev_scc_graph.upper_bounds(scc) {
-            debug!("apply_member_constraint: ub={:?}", ub);
+            debug!(?ub);
             choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r));
         }
-        debug!("apply_member_constraint: after ub, choice_regions={:?}", choice_regions);
+        debug!(?choice_regions, "after ub");
 
         // If we ruled everything out, we're done.
         if choice_regions.is_empty() {
@@ -735,7 +731,7 @@ fn apply_member_constraint(
 
         // Otherwise, we need to find the minimum remaining choice, if
         // any, and take that.
-        debug!("apply_member_constraint: choice_regions remaining are {:#?}", choice_regions);
+        debug!("choice_regions remaining are {:#?}", choice_regions);
         let min = |r1: ty::RegionVid, r2: ty::RegionVid| -> Option<ty::RegionVid> {
             let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2);
             let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1);
@@ -748,27 +744,18 @@ fn apply_member_constraint(
         };
         let mut min_choice = choice_regions[0];
         for &other_option in &choice_regions[1..] {
-            debug!(
-                "apply_member_constraint: min_choice={:?} other_option={:?}",
-                min_choice, other_option,
-            );
+            debug!(?min_choice, ?other_option,);
             match min(min_choice, other_option) {
                 Some(m) => min_choice = m,
                 None => {
-                    debug!(
-                        "apply_member_constraint: {:?} and {:?} are incomparable; no min choice",
-                        min_choice, other_option,
-                    );
+                    debug!(?min_choice, ?other_option, "incomparable; no min choice",);
                     return false;
                 }
             }
         }
 
         let min_choice_scc = self.constraint_sccs.scc(min_choice);
-        debug!(
-            "apply_member_constraint: min_choice={:?} best_choice_scc={:?}",
-            min_choice, min_choice_scc,
-        );
+        debug!(?min_choice, ?min_choice_scc);
         if self.scc_values.add_region(scc, min_choice_scc) {
             self.member_constraints_applied.push(AppliedMemberConstraint {
                 member_region_scc: scc,
@@ -1091,8 +1078,9 @@ fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid {
     ///   include the CFG anyhow.
     /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding
     ///   a result `'y`.
+    #[instrument(skip(self), level = "debug")]
     pub(crate) fn universal_upper_bound(&self, r: RegionVid) -> RegionVid {
-        debug!("universal_upper_bound(r={:?}={})", r, self.region_value_str(r));
+        debug!(r = %self.region_value_str(r));
 
         // Find the smallest universal region that contains all other
         // universal regions within `region`.
@@ -1102,7 +1090,7 @@ pub(crate) fn universal_upper_bound(&self, r: RegionVid) -> RegionVid {
             lub = self.universal_region_relations.postdom_upper_bound(lub, ur);
         }
 
-        debug!("universal_upper_bound: r={:?} lub={:?}", r, lub);
+        debug!(?lub);
 
         lub
     }
@@ -1262,9 +1250,8 @@ fn eval_equal(&self, r1: RegionVid, r2: RegionVid) -> bool {
     }
 
     // Evaluate whether `sup_region: sub_region`.
+    #[instrument(skip(self), level = "debug")]
     fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool {
-        debug!("eval_outlives({:?}: {:?})", sup_region, sub_region);
-
         debug!(
             "eval_outlives: sup_region's value = {:?} universal={:?}",
             self.region_value_str(sup_region),
@@ -1467,6 +1454,10 @@ fn check_polonius_subset_errors(
     ///
     /// Things that are to be propagated are accumulated into the
     /// `outlives_requirements` vector.
+    #[instrument(
+        skip(self, body, propagated_outlives_requirements, errors_buffer),
+        level = "debug"
+    )]
     fn check_universal_region(
         &self,
         body: &Body<'tcx>,
@@ -1474,8 +1465,6 @@ fn check_universal_region(
         propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
         errors_buffer: &mut RegionErrors<'tcx>,
     ) {
-        debug!("check_universal_region(fr={:?})", longer_fr);
-
         let longer_fr_scc = self.constraint_sccs.scc(longer_fr);
 
         // Because this free region must be in the ROOT universe, we
@@ -1880,21 +1869,13 @@ fn check_member_constraints(
     }
 
     /// Finds some region R such that `fr1: R` and `R` is live at `elem`.
+    #[instrument(skip(self), level = "trace")]
     crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
-        debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
-        debug!("find_sub_region_live_at: {:?} is in scc {:?}", fr1, self.constraint_sccs.scc(fr1));
-        debug!(
-            "find_sub_region_live_at: {:?} is in universe {:?}",
-            fr1,
-            self.scc_universes[self.constraint_sccs.scc(fr1)]
-        );
+        trace!(scc = ?self.constraint_sccs.scc(fr1));
+        trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]);
         self.find_constraint_paths_between_regions(fr1, |r| {
             // First look for some `r` such that `fr1: r` and `r` is live at `elem`
-            debug!(
-                "find_sub_region_live_at: liveness_constraints for {:?} are {:?}",
-                r,
-                self.liveness_constraints.region_value_str(r),
-            );
+            trace!(?r, liveness_constraints=?self.liveness_constraints.region_value_str(r));
             self.liveness_constraints.contains(r, elem)
         })
         .or_else(|| {
index 9377473befe32c011b7c92d8374972222ae3c666..20567610f6557895076e7a79ef877d3259df641b 100644 (file)
@@ -7,13 +7,13 @@
 
 /// Replaces all free regions appearing in the MIR with fresh
 /// inference variables, returning the number of variables created.
+#[instrument(skip(infcx, body, promoted), level = "debug")]
 pub fn renumber_mir<'tcx>(
     infcx: &InferCtxt<'_, 'tcx>,
     body: &mut Body<'tcx>,
     promoted: &mut IndexVec<Promoted, Body<'tcx>>,
 ) {
-    debug!("renumber_mir()");
-    debug!("renumber_mir: body.arg_count={:?}", body.arg_count);
+    debug!(?body.arg_count);
 
     let mut visitor = NllVisitor { infcx };
 
@@ -26,12 +26,11 @@ pub fn renumber_mir<'tcx>(
 
 /// Replaces all regions appearing in `value` with fresh inference
 /// variables.
+#[instrument(skip(infcx), level = "debug")]
 pub fn renumber_regions<'tcx, T>(infcx: &InferCtxt<'_, 'tcx>, value: T) -> T
 where
     T: TypeFoldable<'tcx>,
 {
-    debug!("renumber_regions(value={:?})", value);
-
     infcx.tcx.fold_regions(value, &mut false, |_region, _depth| {
         let origin = NllRegionVariableOrigin::Existential { from_forall: false };
         infcx.next_nll_region_var(origin)
@@ -56,12 +55,11 @@ fn tcx(&self) -> TyCtxt<'tcx> {
         self.infcx.tcx
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
-        debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context);
-
         *ty = self.renumber_regions(ty);
 
-        debug!("visit_ty: ty={:?}", ty);
+        debug!(?ty);
     }
 
     fn process_projection_elem(
@@ -80,21 +78,19 @@ fn process_projection_elem(
         None
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn visit_substs(&mut self, substs: &mut SubstsRef<'tcx>, location: Location) {
-        debug!("visit_substs(substs={:?}, location={:?})", substs, location);
-
         *substs = self.renumber_regions(*substs);
 
-        debug!("visit_substs: substs={:?}", substs);
+        debug!(?substs);
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) {
-        debug!("visit_region(region={:?}, location={:?})", region, location);
-
         let old_region = *region;
         *region = self.renumber_regions(&old_region);
 
-        debug!("visit_region: region={:?}", region);
+        debug!(?region);
     }
 
     fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _location: Location) {
index df28fb6e28e06acbcaaaf222ed85e8f26d0a62e2..7d4df59902aedbb759d4aecac35fde788168996d 100644 (file)
@@ -24,6 +24,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     /// **Any `rustc_infer::infer` operations that might generate region
     /// constraints should occur within this method so that those
     /// constraints can be properly localized!**
+    #[instrument(skip(self, category, op), level = "trace")]
     pub(super) fn fully_perform_op<R, Op>(
         &mut self,
         locations: Locations,
@@ -131,14 +132,13 @@ pub(super) fn prove_predicates(
         }
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub(super) fn prove_predicate(
         &mut self,
         predicate: ty::Predicate<'tcx>,
         locations: Locations,
         category: ConstraintCategory,
     ) {
-        debug!("prove_predicate(predicate={:?}, location={:?})", predicate, locations,);
-
         let param_env = self.param_env;
         self.fully_perform_op(
             locations,
@@ -150,11 +150,11 @@ pub(super) fn prove_predicate(
         })
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub(super) fn normalize<T>(&mut self, value: T, location: impl NormalizeLocation) -> T
     where
         T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx,
     {
-        debug!("normalize(value={:?}, location={:?})", value, location);
         let param_env = self.param_env;
         self.fully_perform_op(
             location.to_locations(),
index b020746848535899733852039c543ac07d71164c..ab1a7461b4b9bf5c05149d443af0a0d1f944d7b3 100644 (file)
@@ -53,9 +53,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         }
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
-        debug!("convert_all(query_constraints={:#?})", query_constraints);
-
         let QueryRegionConstraints { outlives, member_constraints } = query_constraints;
 
         // Annoying: to invoke `self.to_region_vid`, we need access to
index 46d30a188edb903ecfd6b2b5b235ef4f155d38dc..24332690bec31c51c269dd79bef5c0a6567dfa41 100644 (file)
@@ -20,6 +20,7 @@
 use super::{Locations, TypeChecker};
 
 impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
+    #[instrument(skip(self, body, universal_regions), level = "debug")]
     pub(super) fn equate_inputs_and_outputs(
         &mut self,
         body: &Body<'tcx>,
@@ -64,10 +65,7 @@ pub(super) fn equate_inputs_and_outputs(
             );
         }
 
-        debug!(
-            "equate_inputs_and_outputs: normalized_input_tys = {:?}, local_decls = {:?}",
-            normalized_input_tys, body.local_decls
-        );
+        debug!(?normalized_input_tys, ?body.local_decls);
 
         // Equate expected input tys with those in the MIR.
         for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() {
@@ -160,9 +158,8 @@ pub(super) fn equate_inputs_and_outputs(
         }
     }
 
+    #[instrument(skip(self, span), level = "debug")]
     fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
-        debug!("equate_normalized_input_or_output(a={:?}, b={:?})", a, b);
-
         if let Err(_) =
             self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
         {
index 4bbeed39e45ae313d9ed5f7191bdb1cab35c75b8..55790bd2daa9be150db33acb103dc982b9a254e9 100644 (file)
@@ -197,6 +197,11 @@ pub(crate) fn type_check<'mir, 'tcx>(
                 .into_iter()
                 .filter_map(|(opaque_type_key, mut decl)| {
                     decl.concrete_ty = infcx.resolve_vars_if_possible(decl.concrete_ty);
+                    trace!(
+                        "finalized opaque type {:?} to {:#?}",
+                        opaque_type_key,
+                        decl.concrete_ty.kind()
+                    );
                     if decl.concrete_ty.has_infer_types_or_consts() {
                         infcx.tcx.sess.delay_span_bug(
                             body.span,
@@ -247,6 +252,18 @@ pub(crate) fn type_check<'mir, 'tcx>(
     MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
 }
 
+#[instrument(
+    skip(
+        infcx,
+        body,
+        promoted,
+        region_bound_pairs,
+        borrowck_context,
+        universal_region_relations,
+        extra
+    ),
+    level = "debug"
+)]
 fn type_check_internal<'a, 'tcx, R>(
     infcx: &'a InferCtxt<'a, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -1114,13 +1131,14 @@ fn check_user_type_annotations(&mut self) {
         }
     }
 
+    #[instrument(skip(self, data), level = "debug")]
     fn push_region_constraints(
         &mut self,
         locations: Locations,
         category: ConstraintCategory,
         data: &QueryRegionConstraints<'tcx>,
     ) {
-        debug!("push_region_constraints: constraints generated at {:?} are {:#?}", locations, data);
+        debug!("constraints generated: {:#?}", data);
 
         constraint_conversion::ConstraintConversion::new(
             self.infcx,
@@ -1180,6 +1198,7 @@ fn eq_types(
         self.relate_types(expected, ty::Variance::Invariant, found, locations, category)
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn relate_type_and_user_type(
         &mut self,
         a: Ty<'tcx>,
@@ -1188,11 +1207,6 @@ fn relate_type_and_user_type(
         locations: Locations,
         category: ConstraintCategory,
     ) -> Fallible<()> {
-        debug!(
-            "relate_type_and_user_type(a={:?}, v={:?}, user_ty={:?}, locations={:?})",
-            a, v, user_ty, locations,
-        );
-
         let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty;
         let mut curr_projected_ty = PlaceTy::from_ty(annotated_type);
 
@@ -1250,6 +1264,7 @@ fn relate_type_and_user_type(
     ///   generics of `foo`). Note that `anon_ty` is not just the opaque type,
     ///   but the entire return type (which may contain opaque types within it).
     /// * `revealed_ty` would be `Box<(T, u32)>`
+    #[instrument(skip(self), level = "debug")]
     fn eq_opaque_type_and_type(
         &mut self,
         revealed_ty: Ty<'tcx>,
@@ -1257,13 +1272,6 @@ fn eq_opaque_type_and_type(
         locations: Locations,
         category: ConstraintCategory,
     ) -> Fallible<()> {
-        debug!(
-            "eq_opaque_type_and_type( \
-             revealed_ty={:?}, \
-             anon_ty={:?})",
-            revealed_ty, anon_ty
-        );
-
         // Fast path for the common case.
         if !anon_ty.has_opaque_types() {
             if let Err(terr) = self.eq_types(anon_ty, revealed_ty, locations, category) {
@@ -1283,7 +1291,7 @@ fn eq_opaque_type_and_type(
         let body = self.body;
         let mir_def_id = body.source.def_id().expect_local();
 
-        debug!("eq_opaque_type_and_type: mir_def_id={:?}", mir_def_id);
+        debug!(?mir_def_id);
         self.fully_perform_op(
             locations,
             category,
@@ -1305,12 +1313,7 @@ fn eq_opaque_type_and_type(
                         anon_ty,
                         locations.span(body),
                     ));
-                    debug!(
-                        "eq_opaque_type_and_type: \
-                         instantiated output_ty={:?} \
-                         revealed_ty={:?}",
-                        output_ty, revealed_ty
-                    );
+                    debug!(?output_ty, ?revealed_ty);
 
                     // Make sure that the inferred types are well-formed. I'm
                     // not entirely sure this is needed (the HIR type check
@@ -1328,7 +1331,7 @@ fn eq_opaque_type_and_type(
                             .eq(output_ty, revealed_ty)?,
                     );
 
-                    debug!("eq_opaque_type_and_type: equated");
+                    debug!("equated");
 
                     Ok(InferOk { value: (), obligations: obligations.into_vec() })
                 },
@@ -1368,8 +1371,8 @@ fn tcx(&self) -> TyCtxt<'tcx> {
         self.infcx.tcx
     }
 
+    #[instrument(skip(self, body, location), level = "debug")]
     fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Location) {
-        debug!("check_stmt: {:?}", stmt);
         let tcx = self.tcx();
         match stmt.kind {
             StatementKind::Assign(box (ref place, ref rv)) => {
@@ -1522,13 +1525,13 @@ fn check_stmt(&mut self, body: &Body<'tcx>, stmt: &Statement<'tcx>, location: Lo
         }
     }
 
+    #[instrument(skip(self, body, term_location), level = "debug")]
     fn check_terminator(
         &mut self,
         body: &Body<'tcx>,
         term: &Terminator<'tcx>,
         term_location: Location,
     ) {
-        debug!("check_terminator: {:?}", term);
         let tcx = self.tcx();
         match term.kind {
             TerminatorKind::Goto { .. }
@@ -2685,9 +2688,10 @@ fn prove_closure_bounds(
         tcx.predicates_of(def_id).instantiate(tcx, substs)
     }
 
+    #[instrument(skip(self, body), level = "debug")]
     fn typeck_mir(&mut self, body: &Body<'tcx>) {
         self.last_span = body.span;
-        debug!("run_on_mir: {:?}", body.span);
+        debug!(?body.span);
 
         for (local, local_decl) in body.local_decls.iter_enumerated() {
             self.check_local(&body, local, local_decl);
index de86d39cc3722e41b3ed67068269598d3da7220e..b788529dc1cd4847628d6b5912c93a8e92d55973 100644 (file)
@@ -17,6 +17,7 @@
 ///
 /// N.B., the type `a` is permitted to have unresolved inference
 /// variables, but not the type `b`.
+#[instrument(skip(infcx, param_env, borrowck_context), level = "debug")]
 pub(super) fn relate_types<'tcx>(
     infcx: &InferCtxt<'_, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -27,7 +28,6 @@ pub(super) fn relate_types<'tcx>(
     category: ConstraintCategory,
     borrowck_context: &mut BorrowCheckContext<'_, 'tcx>,
 ) -> Fallible<()> {
-    debug!("relate_types(a={:?}, v={:?}, b={:?}, locations={:?})", a, v, b, locations);
     TypeRelating::new(
         infcx,
         NllTypeRelatingDelegate::new(
index 7a51293f5cda5411b59f58e48702de53011e7d0b..a0e99267c2b705771b9c9cfee2e11a41c12a5cf1 100755 (executable)
@@ -96,7 +96,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
             stack = &stack[..index + REPORT_SYMBOL_NAMES.len()];
         }
 
-        const ENCODE_METADATA: &str = "rustc_middle::ty::context::TyCtxt::encode_metadata";
+        const ENCODE_METADATA: &str = "rustc_metadata::rmeta::encoder::encode_metadata";
         if let Some(index) = stack.find(ENCODE_METADATA) {
             stack = &stack[..index + ENCODE_METADATA.len()];
         }
index 40cbc5e1a7ee473fd317a4f046b602e11722bde6..32cc50eebe4364ad8c6707757c63e87501304196 100644 (file)
@@ -6,8 +6,8 @@
 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
 use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::EncodedMetadata;
 use rustc_middle::mir::mono::{CodegenUnit, MonoItem};
 use rustc_session::cgu_reuse_tracker::CguReuse;
 use rustc_session::config::{DebugInfo, OutputType};
index 2ceccdd34994d038781f6509bac70cb76f00d086..beb97edf09e895d778a38e9dc973cc2e0f42dd1d 100644 (file)
@@ -30,8 +30,8 @@
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_codegen_ssa::CodegenResults;
 use rustc_errors::ErrorReported;
+use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::EncodedMetadata;
 use rustc_session::config::OutputFilenames;
 use rustc_session::Session;
 
index 9afa999a87d8d20b3687bd29c2add1215b5e40d2..1c8fd0b01d9d94ed346e67bf5ae734bb9d12b50b 100644 (file)
@@ -3,16 +3,20 @@
 use object::write::{Object, StandardSegment, Symbol, SymbolSection};
 use object::{SectionKind, SymbolFlags, SymbolKind, SymbolScope};
 
-use rustc_middle::middle::cstore::EncodedMetadata;
+use rustc_metadata::EncodedMetadata;
 use rustc_middle::ty::TyCtxt;
 
 // Adapted from https://github.com/rust-lang/rust/blob/da573206f87b5510de4b0ee1a9c044127e409bd3/src/librustc_codegen_llvm/base.rs#L47-L112
-pub(crate) fn new_metadata_object(tcx: TyCtxt<'_>, cgu_name: &str, metadata: &EncodedMetadata) -> Vec<u8> {
+pub(crate) fn new_metadata_object(
+    tcx: TyCtxt<'_>,
+    cgu_name: &str,
+    metadata: &EncodedMetadata,
+) -> Vec<u8> {
     use snap::write::FrameEncoder;
     use std::io::Write;
 
     let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
-    FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap();
+    FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
 
     let triple = crate::target_triple(tcx.sess);
 
diff --git a/compiler/rustc_codegen_gcc/.github/workflows/main.yml b/compiler/rustc_codegen_gcc/.github/workflows/main.yml
new file mode 100644 (file)
index 0000000..98bed8e
--- /dev/null
@@ -0,0 +1,96 @@
+name: CI
+
+on:
+  - push
+  - pull_request
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    strategy:
+      fail-fast: false
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Install packages
+      run: sudo apt-get install ninja-build ripgrep
+
+    - name: Download artifact
+      uses: dawidd6/action-download-artifact@v2
+      with:
+          workflow: main.yml
+          name: libgccjit.so
+          path: gcc-build
+          repo: antoyo/gcc
+
+    - name: Setup path to libgccjit
+      run: |
+          echo $(readlink -f gcc-build) > gcc_path
+          ln gcc-build/libgccjit.so gcc-build/libgccjit.so.0
+
+    - name: Set LIBRARY_PATH
+      run: |
+        echo "LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV
+        echo "LD_LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV
+
+    # https://github.com/actions/cache/issues/133
+    - name: Fixup owner of ~/.cargo/
+      # Don't remove the trailing /. It is necessary to follow the symlink.
+      run: sudo chown -R $(whoami):$(id -ng) ~/.cargo/
+
+    - name: Cache cargo installed crates
+      uses: actions/cache@v1.1.2
+      with:
+        path: ~/.cargo/bin
+        key: cargo-installed-crates2-ubuntu-latest
+
+    - name: Cache cargo registry
+      uses: actions/cache@v1
+      with:
+        path: ~/.cargo/registry
+        key: ${{ runner.os }}-cargo-registry2-${{ hashFiles('**/Cargo.lock') }}
+
+    - name: Cache cargo index
+      uses: actions/cache@v1
+      with:
+        path: ~/.cargo/git
+        key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
+
+    - name: Cache cargo target dir
+      uses: actions/cache@v1.1.2
+      with:
+        path: target
+        key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain') }}
+
+    - name: Build
+      run: |
+        ./prepare_build.sh
+        ./build.sh
+        cargo test
+        ./clean_all.sh
+
+    - name: Prepare dependencies
+      run: |
+        git config --global user.email "user@example.com"
+        git config --global user.name "User"
+        ./prepare.sh
+
+    # Compile is a separate step, as the actions-rs/cargo action supports error annotations
+    - name: Compile
+      uses: actions-rs/cargo@v1.0.3
+      with:
+        command: build
+        args: --release
+
+    - name: Test
+      run: |
+        # Enable backtraces for easier debugging
+        export RUST_BACKTRACE=1
+
+        # Reduce amount of benchmark runs as they are slow
+        export COMPILE_RUNS=2
+        export RUN_RUNS=2
+
+        ./test.sh --release
diff --git a/compiler/rustc_codegen_gcc/.gitignore b/compiler/rustc_codegen_gcc/.gitignore
new file mode 100644 (file)
index 0000000..1e2f9e3
--- /dev/null
@@ -0,0 +1,20 @@
+target
+**/*.rs.bk
+*.rlib
+*.o
+perf.data
+perf.data.old
+*.events
+*.string*
+/build_sysroot/sysroot
+/build_sysroot/sysroot_src
+/build_sysroot/Cargo.lock
+/build_sysroot/test_target/Cargo.lock
+/rust
+/simple-raytracer
+/regex
+gimple*
+*asm
+res
+test-backend
+gcc_path
diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock
new file mode 100644 (file)
index 0000000..60a2101
--- /dev/null
@@ -0,0 +1,373 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "ar"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "450575f58f7bee32816abbff470cbc47797397c2a81e0eaced4b98436daf52e1"
+
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "crc32fast"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "fm"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68fda3cff2cce84c19e5dfa5179a4b35d2c0f18b893f108002b8a6a54984acca"
+dependencies = [
+ "regex",
+]
+
+[[package]]
+name = "gccjit"
+version = "1.0.0"
+source = "git+https://github.com/antoyo/gccjit.rs#2d4fea7319f80531b2e5d264fca9f1c498a3a62e"
+dependencies = [
+ "gccjit_sys",
+]
+
+[[package]]
+name = "gccjit_sys"
+version = "0.0.1"
+source = "git+https://github.com/antoyo/gccjit.rs#2d4fea7319f80531b2e5d264fca9f1c498a3a62e"
+dependencies = [
+ "libc 0.1.12",
+]
+
+[[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
+dependencies = [
+ "cfg-if",
+ "libc 0.2.102",
+ "wasi",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc 0.2.102",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "lang_tester"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96bd995a092cac79868250589869b5a5d656b02a02bd74c8ebdc566dc7203090"
+dependencies = [
+ "fm",
+ "getopts",
+ "libc 0.2.102",
+ "num_cpus",
+ "termcolor",
+ "threadpool",
+ "wait-timeout",
+ "walkdir",
+]
+
+[[package]]
+name = "libc"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122"
+
+[[package]]
+name = "libc"
+version = "0.2.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
+name = "num_cpus"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+dependencies = [
+ "hermit-abi",
+ "libc 0.2.102",
+]
+
+[[package]]
+name = "object"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7"
+dependencies = [
+ "crc32fast",
+ "indexmap",
+ "memchr",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+
+[[package]]
+name = "rand"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+dependencies = [
+ "libc 0.2.102",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "rustc_codegen_gcc"
+version = "0.1.0"
+dependencies = [
+ "ar",
+ "gccjit",
+ "lang_tester",
+ "object",
+ "target-lexicon",
+ "tempfile",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d"
+
+[[package]]
+name = "tempfile"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+dependencies = [
+ "cfg-if",
+ "libc 0.2.102",
+ "rand",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc 0.2.102",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml
new file mode 100644 (file)
index 0000000..9e8c195
--- /dev/null
@@ -0,0 +1,51 @@
+[package]
+name = "rustc_codegen_gcc"
+version = "0.1.0"
+authors = ["Antoni Boucher <bouanto@zoho.com>"]
+edition = "2018"
+license = "MIT OR Apache-2.0"
+
+[lib]
+crate-type = ["dylib"]
+
+[[test]]
+name = "lang_tests"
+path = "tests/lib.rs"
+harness = false
+
+[dependencies]
+gccjit = { git = "https://github.com/antoyo/gccjit.rs" }
+
+# Local copy.
+#gccjit = { path = "../gccjit.rs" }
+
+target-lexicon = "0.10.0"
+
+ar = "0.8.0"
+
+[dependencies.object]
+version = "0.25.0"
+default-features = false
+features = ["read", "std", "write"] # We don't need WASM support.
+
+[dev-dependencies]
+lang_tester = "0.3.9"
+tempfile = "3.1.0"
+
+[profile.dev]
+# By compiling dependencies with optimizations, performing tests gets much faster.
+opt-level = 3
+
+[profile.dev.package.rustc_codegen_gcc]
+# Disabling optimizations for cg_gccjit itself makes compilation after a change faster.
+opt-level = 0
+
+# Disable optimizations and debuginfo of build scripts and some of the heavy build deps, as the
+# execution time of build scripts is so fast that optimizing them slows down the total build time.
+[profile.dev.build-override]
+opt-level = 0
+debug = false
+
+[profile.release.build-override]
+opt-level = 0
+debug = false
diff --git a/compiler/rustc_codegen_gcc/LICENSE-APACHE b/compiler/rustc_codegen_gcc/LICENSE-APACHE
new file mode 100644 (file)
index 0000000..1b5ec8b
--- /dev/null
@@ -0,0 +1,176 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
diff --git a/compiler/rustc_codegen_gcc/LICENSE-MIT b/compiler/rustc_codegen_gcc/LICENSE-MIT
new file mode 100644 (file)
index 0000000..31aa793
--- /dev/null
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/compiler/rustc_codegen_gcc/Readme.md b/compiler/rustc_codegen_gcc/Readme.md
new file mode 100644 (file)
index 0000000..709d93c
--- /dev/null
@@ -0,0 +1,135 @@
+# WIP libgccjit codegen backend for rust
+
+This is a GCC codegen for rustc, which means it can be loaded by the existing rustc frontend, but benefits from GCC: more architectures are supported and GCC's optimizations are used.
+
+**Despite its name, libgccjit can be used for ahead-of-time compilation, as is used here.**
+
+## Motivation
+
+The primary goal of this project is to be able to compile Rust code on platforms unsupported by LLVM.
+A secondary goal is to check if using the gcc backend will provide any run-time speed improvement for the programs compiled using rustc.
+
+## Building
+
+**This requires a patched libgccjit in order to work.
+The patches in [this repostory](https://github.com/antoyo/libgccjit-patches) need to be applied.
+(Those patches should work when applied on master, but in case it doesn't work, they are known to work when applied on 079c23cfe079f203d5df83fea8e92a60c7d7e878.)
+You can also use my [fork of gcc](https://github.com/antoyo/gcc) which already includes these patches.**
+
+**Put the path to your custom build of libgccjit in the file `gcc_path`.**
+
+```bash
+$ git clone https://github.com/rust-lang/rustc_codegen_gcc.git
+$ cd rustc_codegen_gcc
+$ ./prepare_build.sh # download and patch sysroot src
+$ ./build.sh --release
+```
+
+To run the tests:
+
+```bash
+$ ./prepare.sh # download and patch sysroot src and install hyperfine for benchmarking
+$ ./test.sh --release
+```
+
+## Usage
+
+`$cg_gccjit_dir` is the directory you cloned this repo into in the following instructions.
+
+### Cargo
+
+```bash
+$ CHANNEL="release" $cg_gccjit_dir/cargo.sh run
+```
+
+If you compiled cg_gccjit in debug mode (aka you didn't pass `--release` to `./test.sh`) you should use `CHANNEL="debug"` instead or omit `CHANNEL="release"` completely.
+
+### Rustc
+
+> You should prefer using the Cargo method.
+
+```bash
+$ rustc +$(cat $cg_gccjit_dir/rust-toolchain) -Cpanic=abort -Zcodegen-backend=$cg_gccjit_dir/target/release/librustc_codegen_gcc.so --sysroot $cg_gccjit_dir/build_sysroot/sysroot my_crate.rs
+```
+
+## Env vars
+
+<dl>
+    <dt>CG_GCCJIT_INCR_CACHE_DISABLED</dt>
+    <dd>Don't cache object files in the incremental cache. Useful during development of cg_gccjit
+    to make it possible to use incremental mode for all analyses performed by rustc without caching
+    object files when their content should have been changed by a change to cg_gccjit.</dd>
+    <dt>CG_GCCJIT_DISPLAY_CG_TIME</dt>
+    <dd>Display the time it took to perform codegen for a crate</dd>
+</dl>
+
+## Debugging
+
+Sometimes, libgccjit will crash and output an error like this:
+
+```
+during RTL pass: expand
+libgccjit.so: error: in expmed_mode_index, at expmed.h:249
+0x7f0da2e61a35 expmed_mode_index
+       ../../../gcc/gcc/expmed.h:249
+0x7f0da2e61aa4 expmed_op_cost_ptr
+       ../../../gcc/gcc/expmed.h:271
+0x7f0da2e620dc sdiv_cost_ptr
+       ../../../gcc/gcc/expmed.h:540
+0x7f0da2e62129 sdiv_cost
+       ../../../gcc/gcc/expmed.h:558
+0x7f0da2e73c12 expand_divmod(int, tree_code, machine_mode, rtx_def*, rtx_def*, rtx_def*, int)
+       ../../../gcc/gcc/expmed.c:4335
+0x7f0da2ea1423 expand_expr_real_2(separate_ops*, rtx_def*, machine_mode, expand_modifier)
+       ../../../gcc/gcc/expr.c:9240
+0x7f0da2cd1a1e expand_gimple_stmt_1
+       ../../../gcc/gcc/cfgexpand.c:3796
+0x7f0da2cd1c30 expand_gimple_stmt
+       ../../../gcc/gcc/cfgexpand.c:3857
+0x7f0da2cd90a9 expand_gimple_basic_block
+       ../../../gcc/gcc/cfgexpand.c:5898
+0x7f0da2cdade8 execute
+       ../../../gcc/gcc/cfgexpand.c:6582
+```
+
+To see the code which causes this error, call the following function:
+
+```c
+gcc_jit_context_dump_to_file(ctxt, "/tmp/output.c", 1 /* update_locations */)
+```
+
+This will create a C-like file and add the locations into the IR pointing to this C file.
+Then, rerun the program and it will output the location in the second line:
+
+```
+libgccjit.so: /tmp/something.c:61322:0: error: in expmed_mode_index, at expmed.h:249
+```
+
+Or add a breakpoint to `add_error` in gdb and print the line number using:
+
+```
+p loc->m_line
+```
+
+### How to use a custom-build rustc
+
+ * Build the stage2 compiler (`rustup toolchain link debug-current build/x86_64-unknown-linux-gnu/stage2`).
+ * Clean and rebuild the codegen with `debug-current` in the file `rust-toolchain`.
+
+### How to build a cross-compiling libgccjit
+
+#### Building libgccjit
+
+ * Follow these instructions: https://preshing.com/20141119/how-to-build-a-gcc-cross-compiler/ with the following changes:
+ * Configure gcc with `../gcc/configure --enable-host-shared --disable-multilib --enable-languages=c,jit,c++ --disable-bootstrap --enable-checking=release --prefix=/opt/m68k-gcc/ --target=m68k-linux --without-headers`.
+ * Some shells, like fish, don't define the environment variable `$MACHTYPE`.
+ * Add `CFLAGS="-Wno-error=attributes -g -O2"` at the end of the configure command for building glibc (`CFLAGS="-Wno-error=attributes -Wno-error=array-parameter -Wno-error=stringop-overflow -Wno-error=array-bounds -g -O2"` for glibc 2.31, which is useful for Debian).
+
+#### Configuring rustc_codegen_gcc
+
+ * Set `TARGET_TRIPLE="m68k-unknown-linux-gnu"` in config.sh.
+ * Since rustc doesn't support this architecture yet, set it back to `TARGET_TRIPLE="mips-unknown-linux-gnu"` (or another target having the same attributes). Alternatively, create a [target specification file](https://book.avr-rust.com/005.1-the-target-specification-json-file.html) (note that the `arch` specified in this file must be supported by the rust compiler).
+ * Set `linker='-Clinker=m68k-linux-gcc'`.
+ * Set the path to the cross-compiling libgccjit in `gcc_path`.
+ * Disable the 128-bit integer types if the target doesn't support them by using `let i128_type = context.new_type::<i64>();` in `context.rs` (same for u128_type).
+ * (might not be necessary) Disable the compilation of libstd.so (and possibly libcore.so?).
diff --git a/compiler/rustc_codegen_gcc/build.sh b/compiler/rustc_codegen_gcc/build.sh
new file mode 100755 (executable)
index 0000000..17a0d2a
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+#set -x
+set -e
+
+if [ -f ./gcc_path ]; then 
+    export GCC_PATH=$(cat gcc_path)
+else
+    echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
+    exit 1
+fi
+
+export LD_LIBRARY_PATH="$GCC_PATH"
+export LIBRARY_PATH="$GCC_PATH"
+
+if [[ "$1" == "--release" ]]; then
+    export CHANNEL='release'
+    CARGO_INCREMENTAL=1 cargo rustc --release
+else
+    echo $LD_LIBRARY_PATH
+    export CHANNEL='debug'
+    cargo rustc
+fi
+
+source config.sh
+
+rm -r target/out || true
+mkdir -p target/out/gccjit
+
+echo "[BUILD] sysroot"
+time ./build_sysroot/build_sysroot.sh $CHANNEL
diff --git a/compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml b/compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml
new file mode 100644 (file)
index 0000000..cfadf47
--- /dev/null
@@ -0,0 +1,19 @@
+[package]
+authors = ["bjorn3 <bjorn3@users.noreply.github.com>"]
+name = "sysroot"
+version = "0.0.0"
+
+[dependencies]
+core = { path = "./sysroot_src/library/core" }
+compiler_builtins = "0.1"
+alloc = { path = "./sysroot_src/library/alloc" }
+std = { path = "./sysroot_src/library/std", features = ["panic_unwind", "backtrace"] }
+test = { path = "./sysroot_src/library/test" }
+
+[patch.crates-io]
+rustc-std-workspace-core = { path = "./sysroot_src/library/rustc-std-workspace-core" }
+rustc-std-workspace-alloc = { path = "./sysroot_src/library/rustc-std-workspace-alloc" }
+rustc-std-workspace-std = { path = "./sysroot_src/library/rustc-std-workspace-std" }
+
+[profile.release]
+debug = true
diff --git a/compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh b/compiler/rustc_codegen_gcc/build_sysroot/build_sysroot.sh
new file mode 100755 (executable)
index 0000000..d1dcf49
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# Requires the CHANNEL env var to be set to `debug` or `release.`
+
+set -e
+cd $(dirname "$0")
+
+pushd ../ >/dev/null
+source ./config.sh
+popd >/dev/null
+
+# Cleanup for previous run
+#     v Clean target dir except for build scripts and incremental cache
+rm -r target/*/{debug,release}/{build,deps,examples,libsysroot*,native} 2>/dev/null || true
+rm Cargo.lock test_target/Cargo.lock 2>/dev/null || true
+rm -r sysroot/ 2>/dev/null || true
+
+# Build libs
+export RUSTFLAGS="$RUSTFLAGS -Z force-unstable-if-unmarked -Cpanic=abort"
+if [[ "$1" == "--release" ]]; then
+    sysroot_channel='release'
+    RUSTFLAGS="$RUSTFLAGS -Zmir-opt-level=3" cargo build --target $TARGET_TRIPLE --release
+else
+    sysroot_channel='debug'
+    cargo build --target $TARGET_TRIPLE
+fi
+
+# Copy files to sysroot
+mkdir -p sysroot/lib/rustlib/$TARGET_TRIPLE/lib/
+cp -r target/$TARGET_TRIPLE/$sysroot_channel/deps/* sysroot/lib/rustlib/$TARGET_TRIPLE/lib/
diff --git a/compiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh b/compiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh
new file mode 100755 (executable)
index 0000000..071e7ed
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/bash
+set -e
+cd $(dirname "$0")
+
+SRC_DIR=$(dirname $(rustup which rustc))"/../lib/rustlib/src/rust/"
+DST_DIR="sysroot_src"
+
+if [ ! -e $SRC_DIR ]; then
+    echo "Please install rust-src component"
+    exit 1
+fi
+
+rm -rf $DST_DIR
+mkdir -p $DST_DIR/library
+cp -r $SRC_DIR/library $DST_DIR/
+
+pushd $DST_DIR
+echo "[GIT] init"
+git init
+echo "[GIT] add"
+git add .
+echo "[GIT] commit"
+
+# This is needed on systems where nothing is configured.
+# git really needs something here, or it will fail.
+# Even using --author is not enough.
+git config user.email || git config user.email "none@example.com"
+git config user.name || git config user.name "None"
+
+git commit -m "Initial commit" -q
+for file in $(ls ../../patches/ | grep -v patcha); do
+echo "[GIT] apply" $file
+git apply ../../patches/$file
+git add -A
+git commit --no-gpg-sign -m "Patch $file"
+done
+popd
+
+echo "Successfully prepared libcore for building"
diff --git a/compiler/rustc_codegen_gcc/build_sysroot/src/lib.rs b/compiler/rustc_codegen_gcc/build_sysroot/src/lib.rs
new file mode 100644 (file)
index 0000000..0c9ac1a
--- /dev/null
@@ -0,0 +1 @@
+#![no_std]
diff --git a/compiler/rustc_codegen_gcc/cargo.sh b/compiler/rustc_codegen_gcc/cargo.sh
new file mode 100755 (executable)
index 0000000..1001c52
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+if [ -z $CHANNEL ]; then
+export CHANNEL='debug'
+fi
+
+pushd $(dirname "$0") >/dev/null
+source config.sh
+
+# read nightly compiler from rust-toolchain file
+TOOLCHAIN=$(cat rust-toolchain)
+
+popd >/dev/null
+
+if [[ $(rustc -V) != $(rustc +${TOOLCHAIN} -V) ]]; then
+    echo "rustc_codegen_gcc is build for $(rustc +${TOOLCHAIN} -V) but the default rustc version is $(rustc -V)."
+    echo "Using $(rustc +${TOOLCHAIN} -V)."
+fi
+
+cmd=$1
+shift
+
+RUSTDOCFLAGS="$RUSTFLAGS" cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@
diff --git a/compiler/rustc_codegen_gcc/clean_all.sh b/compiler/rustc_codegen_gcc/clean_all.sh
new file mode 100755 (executable)
index 0000000..a77d148
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/bash --verbose
+set -e
+
+rm -rf target/ build_sysroot/{sysroot/,sysroot_src/,target/,Cargo.lock} perf.data{,.old}
+rm -rf regex/ simple-raytracer/
diff --git a/compiler/rustc_codegen_gcc/config.sh b/compiler/rustc_codegen_gcc/config.sh
new file mode 100644 (file)
index 0000000..87df2f2
--- /dev/null
@@ -0,0 +1,52 @@
+set -e
+
+export CARGO_INCREMENTAL=0
+
+if [ -f ./gcc_path ]; then 
+    export GCC_PATH=$(cat gcc_path)
+else
+    echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
+    exit 1
+fi
+
+unamestr=`uname`
+if [[ "$unamestr" == 'Linux' ]]; then
+   dylib_ext='so'
+elif [[ "$unamestr" == 'Darwin' ]]; then
+   dylib_ext='dylib'
+else
+   echo "Unsupported os"
+   exit 1
+fi
+
+HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ")
+TARGET_TRIPLE=$HOST_TRIPLE
+#TARGET_TRIPLE="m68k-unknown-linux-gnu"
+
+linker=''
+RUN_WRAPPER=''
+if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then
+   if [[ "$TARGET_TRIPLE" == "m68k-unknown-linux-gnu" ]]; then
+       TARGET_TRIPLE="mips-unknown-linux-gnu"
+       linker='-Clinker=m68k-linux-gcc'
+   elif [[ "$TARGET_TRIPLE" == "aarch64-unknown-linux-gnu" ]]; then
+      # We are cross-compiling for aarch64. Use the correct linker and run tests in qemu.
+      linker='-Clinker=aarch64-linux-gnu-gcc'
+      RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu'
+   else
+      echo "Unknown non-native platform"
+   fi
+fi
+
+export RUSTFLAGS="$linker -Cpanic=abort -Zsymbol-mangling-version=v0 -Cdebuginfo=2 -Clto=off -Zpanic-abort-tests -Zcodegen-backend=$(pwd)/target/${CHANNEL:-debug}/librustc_codegen_gcc.$dylib_ext --sysroot $(pwd)/build_sysroot/sysroot"
+
+# FIXME(antoyo): remove once the atomic shim is gone
+if [[ `uname` == 'Darwin' ]]; then
+   export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup"
+fi
+
+RUSTC="rustc $RUSTFLAGS -L crate=target/out --out-dir target/out"
+export RUSTC_LOG=warn # display metadata load errors
+
+export LD_LIBRARY_PATH="$(pwd)/target/out:$(pwd)/build_sysroot/sysroot/lib/rustlib/$TARGET_TRIPLE/lib:$GCC_PATH"
+export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH
diff --git a/compiler/rustc_codegen_gcc/example/alloc_example.rs b/compiler/rustc_codegen_gcc/example/alloc_example.rs
new file mode 100644 (file)
index 0000000..bc6dd00
--- /dev/null
@@ -0,0 +1,41 @@
+#![feature(start, box_syntax, core_intrinsics, alloc_prelude, alloc_error_handler)]
+#![no_std]
+
+extern crate alloc;
+extern crate alloc_system;
+
+use alloc::prelude::v1::*;
+
+use alloc_system::System;
+
+#[global_allocator]
+static ALLOC: System = System;
+
+#[link(name = "c")]
+extern "C" {
+    fn puts(s: *const u8) -> i32;
+}
+
+#[panic_handler]
+fn panic_handler(_: &core::panic::PanicInfo) -> ! {
+    unsafe {
+        core::intrinsics::abort();
+    }
+}
+
+#[alloc_error_handler]
+fn alloc_error_handler(_: alloc::alloc::Layout) -> ! {
+    unsafe {
+        core::intrinsics::abort();
+    }
+}
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+    let world: Box<&str> = box "Hello World!\0";
+    unsafe {
+        puts(*world as *const str as *const u8);
+    }
+
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/example/alloc_system.rs b/compiler/rustc_codegen_gcc/example/alloc_system.rs
new file mode 100644 (file)
index 0000000..5f66ca6
--- /dev/null
@@ -0,0 +1,212 @@
+// 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.
+#![no_std]
+#![feature(allocator_api, rustc_private)]
+#![cfg_attr(any(unix, target_os = "redox"), feature(libc))]
+
+// The minimum alignment guaranteed by the architecture. This value is used to
+// add fast paths for low alignment values.
+#[cfg(all(any(target_arch = "x86",
+              target_arch = "arm",
+              target_arch = "mips",
+              target_arch = "powerpc",
+              target_arch = "powerpc64")))]
+const MIN_ALIGN: usize = 8;
+#[cfg(all(any(target_arch = "x86_64",
+              target_arch = "aarch64",
+              target_arch = "mips64",
+              target_arch = "s390x",
+              target_arch = "sparc64")))]
+const MIN_ALIGN: usize = 16;
+
+pub struct System;
+#[cfg(any(windows, unix, target_os = "redox"))]
+mod realloc_fallback {
+    use core::alloc::{GlobalAlloc, Layout};
+    use core::cmp;
+    use core::ptr;
+    impl super::System {
+        pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut u8, old_layout: Layout,
+                                              new_size: usize) -> *mut u8 {
+            // Docs for GlobalAlloc::realloc require this to be valid:
+            let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
+            let new_ptr = GlobalAlloc::alloc(self, new_layout);
+            if !new_ptr.is_null() {
+                let size = cmp::min(old_layout.size(), new_size);
+                ptr::copy_nonoverlapping(ptr, new_ptr, size);
+                GlobalAlloc::dealloc(self, ptr, old_layout);
+            }
+            new_ptr
+        }
+    }
+}
+#[cfg(any(unix, target_os = "redox"))]
+mod platform {
+    extern crate libc;
+    use core::ptr;
+    use MIN_ALIGN;
+    use System;
+    use core::alloc::{GlobalAlloc, Layout};
+    unsafe impl GlobalAlloc for System {
+        #[inline]
+        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+            if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+                libc::malloc(layout.size()) as *mut u8
+            } else {
+                #[cfg(target_os = "macos")]
+                {
+                    if layout.align() > (1 << 31) {
+                        return ptr::null_mut()
+                    }
+                }
+                aligned_malloc(&layout)
+            }
+        }
+        #[inline]
+        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+            if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+                libc::calloc(layout.size(), 1) as *mut u8
+            } else {
+                let ptr = self.alloc(layout.clone());
+                if !ptr.is_null() {
+                    ptr::write_bytes(ptr, 0, layout.size());
+                }
+                ptr
+            }
+        }
+        #[inline]
+        unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+            libc::free(ptr as *mut libc::c_void)
+        }
+        #[inline]
+        unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+            if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
+                libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
+            } else {
+                self.realloc_fallback(ptr, layout, new_size)
+            }
+        }
+    }
+    #[cfg(any(target_os = "android",
+              target_os = "hermit",
+              target_os = "redox",
+              target_os = "solaris"))]
+    #[inline]
+    unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+        // On android we currently target API level 9 which unfortunately
+        // doesn't have the `posix_memalign` API used below. Instead we use
+        // `memalign`, but this unfortunately has the property on some systems
+        // where the memory returned cannot be deallocated by `free`!
+        //
+        // Upon closer inspection, however, this appears to work just fine with
+        // Android, so for this platform we should be fine to call `memalign`
+        // (which is present in API level 9). Some helpful references could
+        // possibly be chromium using memalign [1], attempts at documenting that
+        // memalign + free is ok [2] [3], or the current source of chromium
+        // which still uses memalign on android [4].
+        //
+        // [1]: https://codereview.chromium.org/10796020/
+        // [2]: https://code.google.com/p/android/issues/detail?id=35391
+        // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
+        // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
+        //                                       /memory/aligned_memory.cc
+        libc::memalign(layout.align(), layout.size()) as *mut u8
+    }
+    #[cfg(not(any(target_os = "android",
+                  target_os = "hermit",
+                  target_os = "redox",
+                  target_os = "solaris")))]
+    #[inline]
+    unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+        let mut out = ptr::null_mut();
+        let ret = libc::posix_memalign(&mut out, layout.align(), layout.size());
+        if ret != 0 {
+            ptr::null_mut()
+        } else {
+            out as *mut u8
+        }
+    }
+}
+#[cfg(windows)]
+#[allow(nonstandard_style)]
+mod platform {
+    use MIN_ALIGN;
+    use System;
+    use core::alloc::{GlobalAlloc, Layout};
+    type LPVOID = *mut u8;
+    type HANDLE = LPVOID;
+    type SIZE_T = usize;
+    type DWORD = u32;
+    type BOOL = i32;
+    extern "system" {
+        fn GetProcessHeap() -> HANDLE;
+        fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
+        fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID;
+        fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
+        fn GetLastError() -> DWORD;
+    }
+    #[repr(C)]
+    struct Header(*mut u8);
+    const HEAP_ZERO_MEMORY: DWORD = 0x00000008;
+    unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header {
+        &mut *(ptr as *mut Header).offset(-1)
+    }
+    unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 {
+        let aligned = ptr.add(align - (ptr as usize & (align - 1)));
+        *get_header(aligned) = Header(ptr);
+        aligned
+    }
+    #[inline]
+    unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 {
+        let ptr = if layout.align() <= MIN_ALIGN {
+            HeapAlloc(GetProcessHeap(), flags, layout.size())
+        } else {
+            let size = layout.size() + layout.align();
+            let ptr = HeapAlloc(GetProcessHeap(), flags, size);
+            if ptr.is_null() {
+                ptr
+            } else {
+                align_ptr(ptr, layout.align())
+            }
+        };
+        ptr as *mut u8
+    }
+    unsafe impl GlobalAlloc for System {
+        #[inline]
+        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+            allocate_with_flags(layout, 0)
+        }
+        #[inline]
+        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+            allocate_with_flags(layout, HEAP_ZERO_MEMORY)
+        }
+        #[inline]
+        unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+            if layout.align() <= MIN_ALIGN {
+                let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID);
+                debug_assert!(err != 0, "Failed to free heap memory: {}",
+                              GetLastError());
+            } else {
+                let header = get_header(ptr);
+                let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID);
+                debug_assert!(err != 0, "Failed to free heap memory: {}",
+                              GetLastError());
+            }
+        }
+        #[inline]
+        unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+            if layout.align() <= MIN_ALIGN {
+                HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8
+            } else {
+                self.realloc_fallback(ptr, layout, new_size)
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs b/compiler/rustc_codegen_gcc/example/arbitrary_self_types_pointers_and_wrappers.rs
new file mode 100644 (file)
index 0000000..ddeb752
--- /dev/null
@@ -0,0 +1,69 @@
+// Adapted from rustc run-pass test suite
+
+#![feature(arbitrary_self_types, unsize, coerce_unsized, dispatch_from_dyn)]
+#![feature(rustc_attrs)]
+
+use std::{
+    ops::{Deref, CoerceUnsized, DispatchFromDyn},
+    marker::Unsize,
+};
+
+struct Ptr<T: ?Sized>(Box<T>);
+
+impl<T: ?Sized> Deref for Ptr<T> {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        &*self.0
+    }
+}
+
+impl<T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T> {}
+impl<T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<Ptr<U>> for Ptr<T> {}
+
+struct Wrapper<T: ?Sized>(T);
+
+impl<T: ?Sized> Deref for Wrapper<T> {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        &self.0
+    }
+}
+
+impl<T: CoerceUnsized<U>, U> CoerceUnsized<Wrapper<U>> for Wrapper<T> {}
+impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Wrapper<U>> for Wrapper<T> {}
+
+
+trait Trait {
+    // This method isn't object-safe yet. Unsized by-value `self` is object-safe (but not callable
+    // without unsized_locals), but wrappers arond `Self` currently are not.
+    // FIXME (mikeyhew) uncomment this when unsized rvalues object-safety is implemented
+    // fn wrapper(self: Wrapper<Self>) -> i32;
+    fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32;
+    fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32;
+    fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32;
+}
+
+impl Trait for i32 {
+    fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32 {
+        **self
+    }
+    fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32 {
+        **self
+    }
+    fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32 {
+        ***self
+    }
+}
+
+fn main() {
+    let pw = Ptr(Box::new(Wrapper(5))) as Ptr<Wrapper<dyn Trait>>;
+    assert_eq!(pw.ptr_wrapper(), 5);
+
+    let wp = Wrapper(Ptr(Box::new(6))) as Wrapper<Ptr<dyn Trait>>;
+    assert_eq!(wp.wrapper_ptr(), 6);
+
+    let wpw = Wrapper(Ptr(Box::new(Wrapper(7)))) as Wrapper<Ptr<Wrapper<dyn Trait>>>;
+    assert_eq!(wpw.wrapper_ptr_wrapper(), 7);
+}
diff --git a/compiler/rustc_codegen_gcc/example/dst-field-align.rs b/compiler/rustc_codegen_gcc/example/dst-field-align.rs
new file mode 100644 (file)
index 0000000..6c338e9
--- /dev/null
@@ -0,0 +1,67 @@
+// run-pass
+#![allow(dead_code)]
+struct Foo<T: ?Sized> {
+    a: u16,
+    b: T
+}
+
+trait Bar {
+    fn get(&self) -> usize;
+}
+
+impl Bar for usize {
+    fn get(&self) -> usize { *self }
+}
+
+struct Baz<T: ?Sized> {
+    a: T
+}
+
+struct HasDrop<T: ?Sized> {
+    ptr: Box<usize>,
+    data: T
+}
+
+fn main() {
+    // Test that zero-offset works properly
+    let b : Baz<usize> = Baz { a: 7 };
+    assert_eq!(b.a.get(), 7);
+    let b : &Baz<dyn Bar> = &b;
+    assert_eq!(b.a.get(), 7);
+
+    // Test that the field is aligned properly
+    let f : Foo<usize> = Foo { a: 0, b: 11 };
+    assert_eq!(f.b.get(), 11);
+    let ptr1 : *const u8 = &f.b as *const _ as *const u8;
+
+    let f : &Foo<dyn Bar> = &f;
+    let ptr2 : *const u8 = &f.b as *const _ as *const u8;
+    assert_eq!(f.b.get(), 11);
+
+    // The pointers should be the same
+    assert_eq!(ptr1, ptr2);
+
+    // Test that nested DSTs work properly
+    let f : Foo<Foo<usize>> = Foo { a: 0, b: Foo { a: 1, b: 17 }};
+    assert_eq!(f.b.b.get(), 17);
+    let f : &Foo<Foo<dyn Bar>> = &f;
+    assert_eq!(f.b.b.get(), 17);
+
+    // Test that get the pointer via destructuring works
+
+    let f : Foo<usize> = Foo { a: 0, b: 11 };
+    let f : &Foo<dyn Bar> = &f;
+    let &Foo { a: _, b: ref bar } = f;
+    assert_eq!(bar.get(), 11);
+
+    // Make sure that drop flags don't screw things up
+
+    let d : HasDrop<Baz<[i32; 4]>> = HasDrop {
+        ptr: Box::new(0),
+        data: Baz { a: [1,2,3,4] }
+    };
+    assert_eq!([1,2,3,4], d.data.a);
+
+    let d : &HasDrop<Baz<[i32]>> = &d;
+    assert_eq!(&[1,2,3,4], &d.data.a);
+}
diff --git a/compiler/rustc_codegen_gcc/example/example.rs b/compiler/rustc_codegen_gcc/example/example.rs
new file mode 100644 (file)
index 0000000..5878e85
--- /dev/null
@@ -0,0 +1,208 @@
+#![feature(no_core, unboxed_closures)]
+#![no_core]
+#![allow(dead_code)]
+
+extern crate mini_core;
+
+use mini_core::*;
+
+fn abc(a: u8) -> u8 {
+    a * 2
+}
+
+fn bcd(b: bool, a: u8) -> u8 {
+    if b {
+        a * 2
+    } else {
+        a * 3
+    }
+}
+
+fn call() {
+    abc(42);
+}
+
+fn indirect_call() {
+    let f: fn() = call;
+    f();
+}
+
+enum BoolOption {
+    Some(bool),
+    None,
+}
+
+fn option_unwrap_or(o: BoolOption, d: bool) -> bool {
+    match o {
+        BoolOption::Some(b) => b,
+        BoolOption::None => d,
+    }
+}
+
+fn ret_42() -> u8 {
+    42
+}
+
+fn return_str() -> &'static str {
+    "hello world"
+}
+
+fn promoted_val() -> &'static u8 {
+    &(1 * 2)
+}
+
+fn cast_ref_to_raw_ptr(abc: &u8) -> *const u8 {
+    abc as *const u8
+}
+
+fn cmp_raw_ptr(a: *const u8, b: *const u8) -> bool {
+    a == b
+}
+
+fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u32) {
+    (
+        a as u8, a as u16, a as u32, a as usize, a as i8, a as i16, a as i32, a as isize, b as u8,
+        b as u32,
+    )
+}
+
+fn char_cast(c: char) -> u8 {
+    c as u8
+}
+
+pub struct DebugTuple(());
+
+fn debug_tuple() -> DebugTuple {
+    DebugTuple(())
+}
+
+fn size_of<T>() -> usize {
+    intrinsics::size_of::<T>()
+}
+
+fn use_size_of() -> usize {
+    size_of::<u64>()
+}
+
+unsafe fn use_copy_intrinsic(src: *const u8, dst: *mut u8) {
+    intrinsics::copy::<u8>(src, dst, 1);
+}
+
+unsafe fn use_copy_intrinsic_ref(src: *const u8, dst: *mut u8) {
+    let copy2 = &intrinsics::copy::<u8>;
+    copy2(src, dst, 1);
+}
+
+const ABC: u8 = 6 * 7;
+
+fn use_const() -> u8 {
+    ABC
+}
+
+pub fn call_closure_3arg() {
+    (|_, _, _| {})(0u8, 42u16, 0u8)
+}
+
+pub fn call_closure_2arg() {
+    (|_, _| {})(0u8, 42u16)
+}
+
+struct IsNotEmpty;
+
+impl<'a, 'b> FnOnce<(&'a &'b [u16],)> for IsNotEmpty {
+    type Output = (u8, u8);
+
+    #[inline]
+    extern "rust-call" fn call_once(mut self, arg: (&'a &'b [u16],)) -> (u8, u8) {
+        self.call_mut(arg)
+    }
+}
+
+impl<'a, 'b> FnMut<(&'a &'b [u16],)> for IsNotEmpty {
+    #[inline]
+    extern "rust-call" fn call_mut(&mut self, _arg: (&'a &'b [u16],)) -> (u8, u8) {
+        (0, 42)
+    }
+}
+
+pub fn call_is_not_empty() {
+    IsNotEmpty.call_once((&(&[0u16] as &[_]),));
+}
+
+fn eq_char(a: char, b: char) -> bool {
+    a == b
+}
+
+unsafe fn transmute(c: char) -> u32 {
+    intrinsics::transmute(c)
+}
+
+unsafe fn deref_str_ptr(s: *const str) -> &'static str {
+    &*s
+}
+
+fn use_array(arr: [u8; 3]) -> u8 {
+    arr[1]
+}
+
+fn repeat_array() -> [u8; 3] {
+    [0; 3]
+}
+
+fn array_as_slice(arr: &[u8; 3]) -> &[u8] {
+    arr
+}
+
+unsafe fn use_ctlz_nonzero(a: u16) -> u16 {
+    intrinsics::ctlz_nonzero(a)
+}
+
+fn ptr_as_usize(ptr: *const u8) -> usize {
+    ptr as usize
+}
+
+fn float_cast(a: f32, b: f64) -> (f64, f32) {
+    (a as f64, b as f32)
+}
+
+fn int_to_float(a: u8, b: i32) -> (f64, f32) {
+    (a as f64, b as f32)
+}
+
+fn make_array() -> [u8; 3] {
+    [42, 0, 5]
+}
+
+fn some_promoted_tuple() -> &'static (&'static str, &'static str) {
+    &("abc", "some")
+}
+
+fn index_slice(s: &[u8]) -> u8 {
+    s[2]
+}
+
+pub struct StrWrapper {
+    s: str,
+}
+
+fn str_wrapper_get(w: &StrWrapper) -> &str {
+    &w.s
+}
+
+fn i16_as_i8(a: i16) -> i8 {
+    a as i8
+}
+
+struct Unsized(u8, str);
+
+fn get_sized_field_ref_from_unsized_type(u: &Unsized) -> &u8 {
+    &u.0
+}
+
+fn get_unsized_field_ref_from_unsized_type(u: &Unsized) -> &str {
+    &u.1
+}
+
+pub fn reuse_byref_argument_storage(a: (u8, u16, u32)) -> u8 {
+    a.0
+}
diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs
new file mode 100644 (file)
index 0000000..1067cee
--- /dev/null
@@ -0,0 +1,585 @@
+#![feature(
+    no_core, lang_items, intrinsics, unboxed_closures, type_ascription, extern_types,
+    untagged_unions, decl_macro, rustc_attrs, transparent_unions, auto_traits,
+    thread_local
+)]
+#![no_core]
+#![allow(dead_code)]
+
+#[no_mangle]
+unsafe extern "C" fn _Unwind_Resume() {
+    intrinsics::unreachable();
+}
+
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "unsize"]
+pub trait Unsize<T: ?Sized> {}
+
+#[lang = "coerce_unsized"]
+pub trait CoerceUnsized<T> {}
+
+impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+
+#[lang = "dispatch_from_dyn"]
+pub trait DispatchFromDyn<T> {}
+
+// &T -> &U
+impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
+// &mut T -> &mut U
+impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
+// *const T -> *const U
+impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
+// *mut T -> *mut U
+impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {}
+
+#[lang = "receiver"]
+pub trait Receiver {}
+
+impl<T: ?Sized> Receiver for &T {}
+impl<T: ?Sized> Receiver for &mut T {}
+impl<T: ?Sized> Receiver for Box<T> {}
+
+#[lang = "copy"]
+pub unsafe trait Copy {}
+
+unsafe impl Copy for bool {}
+unsafe impl Copy for u8 {}
+unsafe impl Copy for u16 {}
+unsafe impl Copy for u32 {}
+unsafe impl Copy for u64 {}
+unsafe impl Copy for usize {}
+unsafe impl Copy for i8 {}
+unsafe impl Copy for i16 {}
+unsafe impl Copy for i32 {}
+unsafe impl Copy for isize {}
+unsafe impl Copy for f32 {}
+unsafe impl Copy for char {}
+unsafe impl<'a, T: ?Sized> Copy for &'a T {}
+unsafe impl<T: ?Sized> Copy for *const T {}
+unsafe impl<T: ?Sized> Copy for *mut T {}
+
+#[lang = "sync"]
+pub unsafe trait Sync {}
+
+unsafe impl Sync for bool {}
+unsafe impl Sync for u8 {}
+unsafe impl Sync for u16 {}
+unsafe impl Sync for u32 {}
+unsafe impl Sync for u64 {}
+unsafe impl Sync for usize {}
+unsafe impl Sync for i8 {}
+unsafe impl Sync for i16 {}
+unsafe impl Sync for i32 {}
+unsafe impl Sync for isize {}
+unsafe impl Sync for char {}
+unsafe impl<'a, T: ?Sized> Sync for &'a T {}
+unsafe impl Sync for [u8; 16] {}
+
+#[lang = "freeze"]
+unsafe auto trait Freeze {}
+
+unsafe impl<T: ?Sized> Freeze for PhantomData<T> {}
+unsafe impl<T: ?Sized> Freeze for *const T {}
+unsafe impl<T: ?Sized> Freeze for *mut T {}
+unsafe impl<T: ?Sized> Freeze for &T {}
+unsafe impl<T: ?Sized> Freeze for &mut T {}
+
+#[lang = "structural_peq"]
+pub trait StructuralPartialEq {}
+
+#[lang = "structural_teq"]
+pub trait StructuralEq {}
+
+#[lang = "not"]
+pub trait Not {
+    type Output;
+
+    fn not(self) -> Self::Output;
+}
+
+impl Not for bool {
+    type Output = bool;
+
+    fn not(self) -> bool {
+        !self
+    }
+}
+
+#[lang = "mul"]
+pub trait Mul<RHS = Self> {
+    type Output;
+
+    #[must_use]
+    fn mul(self, rhs: RHS) -> Self::Output;
+}
+
+impl Mul for u8 {
+    type Output = Self;
+
+    fn mul(self, rhs: Self) -> Self::Output {
+        self * rhs
+    }
+}
+
+impl Mul for usize {
+    type Output = Self;
+
+    fn mul(self, rhs: Self) -> Self::Output {
+        self * rhs
+    }
+}
+
+#[lang = "add"]
+pub trait Add<RHS = Self> {
+    type Output;
+
+    fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for usize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+    type Output;
+
+    fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for u8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i16 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+#[lang = "rem"]
+pub trait Rem<RHS = Self> {
+    type Output;
+
+    fn rem(self, rhs: RHS) -> Self::Output;
+}
+
+impl Rem for usize {
+    type Output = Self;
+
+    fn rem(self, rhs: Self) -> Self {
+        self % rhs
+    }
+}
+
+#[lang = "bitor"]
+pub trait BitOr<RHS = Self> {
+    type Output;
+
+    #[must_use]
+    fn bitor(self, rhs: RHS) -> Self::Output;
+}
+
+impl BitOr for bool {
+    type Output = bool;
+
+    fn bitor(self, rhs: bool) -> bool {
+        self | rhs
+    }
+}
+
+impl<'a> BitOr<bool> for &'a bool {
+    type Output = bool;
+
+    fn bitor(self, rhs: bool) -> bool {
+        *self | rhs
+    }
+}
+
+#[lang = "eq"]
+pub trait PartialEq<Rhs: ?Sized = Self> {
+    fn eq(&self, other: &Rhs) -> bool;
+    fn ne(&self, other: &Rhs) -> bool;
+}
+
+impl PartialEq for u8 {
+    fn eq(&self, other: &u8) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &u8) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for u16 {
+    fn eq(&self, other: &u16) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &u16) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for u32 {
+    fn eq(&self, other: &u32) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &u32) -> bool {
+        (*self) != (*other)
+    }
+}
+
+
+impl PartialEq for u64 {
+    fn eq(&self, other: &u64) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &u64) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for usize {
+    fn eq(&self, other: &usize) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &usize) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for i8 {
+    fn eq(&self, other: &i8) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &i8) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for i32 {
+    fn eq(&self, other: &i32) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &i32) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for isize {
+    fn eq(&self, other: &isize) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &isize) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for char {
+    fn eq(&self, other: &char) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &char) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl<T: ?Sized> PartialEq for *const T {
+    fn eq(&self, other: &*const T) -> bool {
+        *self == *other
+    }
+    fn ne(&self, other: &*const T) -> bool {
+        *self != *other
+    }
+}
+
+#[lang = "neg"]
+pub trait Neg {
+    type Output;
+
+    fn neg(self) -> Self::Output;
+}
+
+impl Neg for i8 {
+    type Output = i8;
+
+    fn neg(self) -> i8 {
+        -self
+    }
+}
+
+impl Neg for i16 {
+    type Output = i16;
+
+    fn neg(self) -> i16 {
+        self
+    }
+}
+
+impl Neg for isize {
+    type Output = isize;
+
+    fn neg(self) -> isize {
+        -self
+    }
+}
+
+impl Neg for f32 {
+    type Output = f32;
+
+    fn neg(self) -> f32 {
+        -self
+    }
+}
+
+pub enum Option<T> {
+    Some(T),
+    None,
+}
+
+pub use Option::*;
+
+#[lang = "phantom_data"]
+pub struct PhantomData<T: ?Sized>;
+
+#[lang = "fn_once"]
+#[rustc_paren_sugar]
+pub trait FnOnce<Args> {
+    #[lang = "fn_once_output"]
+    type Output;
+
+    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+}
+
+#[lang = "fn_mut"]
+#[rustc_paren_sugar]
+pub trait FnMut<Args>: FnOnce<Args> {
+    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
+}
+
+#[lang = "panic"]
+#[track_caller]
+pub fn panic(_msg: &str) -> ! {
+    unsafe {
+        libc::puts("Panicking\n\0" as *const str as *const u8);
+        intrinsics::abort();
+    }
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+    unsafe {
+        libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+        intrinsics::abort();
+    }
+}
+
+#[lang = "eh_personality"]
+fn eh_personality() -> ! {
+    loop {}
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+    // Code here does not matter - this is replaced by the
+    // real drop glue by the compiler.
+    drop_in_place(to_drop);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target: ?Sized;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+#[lang = "owned_box"]
+pub struct Box<T: ?Sized>(*mut T);
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
+
+impl<T: ?Sized> Drop for Box<T> {
+    fn drop(&mut self) {
+        // drop is currently performed by compiler.
+    }
+}
+
+impl<T> Deref for Box<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &**self
+    }
+}
+
+#[lang = "exchange_malloc"]
+unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
+    libc::malloc(size)
+}
+
+#[lang = "box_free"]
+unsafe fn box_free<T: ?Sized>(ptr: *mut T) {
+    libc::free(ptr as *mut u8);
+}
+
+#[lang = "drop"]
+pub trait Drop {
+    fn drop(&mut self);
+}
+
+#[lang = "manually_drop"]
+#[repr(transparent)]
+pub struct ManuallyDrop<T: ?Sized> {
+    pub value: T,
+}
+
+#[lang = "maybe_uninit"]
+#[repr(transparent)]
+pub union MaybeUninit<T> {
+    pub uninit: (),
+    pub value: ManuallyDrop<T>,
+}
+
+pub mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+        pub fn size_of<T>() -> usize;
+        pub fn size_of_val<T: ?::Sized>(val: *const T) -> usize;
+        pub fn min_align_of<T>() -> usize;
+        pub fn min_align_of_val<T: ?::Sized>(val: *const T) -> usize;
+        pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
+        pub fn transmute<T, U>(e: T) -> U;
+        pub fn ctlz_nonzero<T>(x: T) -> T;
+        pub fn needs_drop<T>() -> bool;
+        pub fn bitreverse<T>(x: T) -> T;
+        pub fn bswap<T>(x: T) -> T;
+        pub fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
+        pub fn unreachable() -> !;
+    }
+}
+
+pub mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn puts(s: *const u8) -> i32;
+        pub fn printf(format: *const i8, ...) -> i32;
+        pub fn malloc(size: usize) -> *mut u8;
+        pub fn free(ptr: *mut u8);
+        pub fn memcpy(dst: *mut u8, src: *const u8, size: usize);
+        pub fn memmove(dst: *mut u8, src: *const u8, size: usize);
+        pub fn strncpy(dst: *mut u8, src: *const u8, size: usize);
+    }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+    type Output: ?Sized;
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+impl<T> Index<usize> for [T] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+extern {
+    type VaListImpl;
+}
+
+#[lang = "va_list"]
+#[repr(transparent)]
+pub struct VaList<'a>(&'a mut VaListImpl);
+
+#[rustc_builtin_macro]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro stringify($($t:tt)*) { /* compiler built-in */ }
+
+#[rustc_builtin_macro]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro file() { /* compiler built-in */ }
+
+#[rustc_builtin_macro]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro line() { /* compiler built-in */ }
+
+#[rustc_builtin_macro]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro cfg() { /* compiler built-in */ }
+
+pub static A_STATIC: u8 = 42;
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+#[no_mangle]
+pub fn get_tls() -> u8 {
+    #[thread_local]
+    static A: u8 = 42;
+
+    A
+}
diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
new file mode 100644 (file)
index 0000000..69d5915
--- /dev/null
@@ -0,0 +1,424 @@
+// Adapted from https://github.com/sunfishcode/mir2cranelift/blob/master/rust-examples/nocore-hello-world.rs
+
+#![feature(
+    no_core, unboxed_closures, start, lang_items, box_syntax, never_type, linkage,
+    extern_types, thread_local
+)]
+#![no_core]
+#![allow(dead_code, non_camel_case_types)]
+
+extern crate mini_core;
+
+use mini_core::*;
+use mini_core::libc::*;
+
+unsafe extern "C" fn my_puts(s: *const u8) {
+    puts(s);
+}
+
+#[lang = "termination"]
+trait Termination {
+    fn report(self) -> i32;
+}
+
+impl Termination for () {
+    fn report(self) -> i32 {
+        unsafe {
+            NUM = 6 * 7 + 1 + (1u8 == 1u8) as u8; // 44
+            *NUM_REF as i32
+        }
+    }
+}
+
+trait SomeTrait {
+    fn object_safe(&self);
+}
+
+impl SomeTrait for &'static str {
+    fn object_safe(&self) {
+        unsafe {
+            puts(*self as *const str as *const u8);
+        }
+    }
+}
+
+struct NoisyDrop {
+    text: &'static str,
+    inner: NoisyDropInner,
+}
+
+struct NoisyDropInner;
+
+impl Drop for NoisyDrop {
+    fn drop(&mut self) {
+        unsafe {
+            puts(self.text as *const str as *const u8);
+        }
+    }
+}
+
+impl Drop for NoisyDropInner {
+    fn drop(&mut self) {
+        unsafe {
+            puts("Inner got dropped!\0" as *const str as *const u8);
+        }
+    }
+}
+
+impl SomeTrait for NoisyDrop {
+    fn object_safe(&self) {}
+}
+
+enum Ordering {
+    Less = -1,
+    Equal = 0,
+    Greater = 1,
+}
+
+#[lang = "start"]
+fn start<T: Termination + 'static>(
+    main: fn() -> T,
+    argc: isize,
+    argv: *const *const u8,
+) -> isize {
+    if argc == 3 {
+        unsafe { puts(*argv); }
+        unsafe { puts(*((argv as usize + intrinsics::size_of::<*const u8>()) as *const *const u8)); }
+        unsafe { puts(*((argv as usize + 2 * intrinsics::size_of::<*const u8>()) as *const *const u8)); }
+    }
+
+    main().report();
+    0
+}
+
+static mut NUM: u8 = 6 * 7;
+static NUM_REF: &'static u8 = unsafe { &NUM };
+
+macro_rules! assert {
+    ($e:expr) => {
+        if !$e {
+            panic(stringify!(! $e));
+        }
+    };
+}
+
+macro_rules! assert_eq {
+    ($l:expr, $r: expr) => {
+        if $l != $r {
+            panic(stringify!($l != $r));
+        }
+    }
+}
+
+struct Unique<T: ?Sized> {
+    pointer: *const T,
+    _marker: PhantomData<T>,
+}
+
+impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {}
+
+unsafe fn zeroed<T>() -> T {
+    let mut uninit = MaybeUninit { uninit: () };
+    intrinsics::write_bytes(&mut uninit.value.value as *mut T, 0, 1);
+    uninit.value.value
+}
+
+fn take_f32(_f: f32) {}
+fn take_unique(_u: Unique<()>) {}
+
+fn return_u128_pair() -> (u128, u128) {
+    (0, 0)
+}
+
+fn call_return_u128_pair() {
+    return_u128_pair();
+}
+
+fn main() {
+    take_unique(Unique {
+        pointer: 0 as *const (),
+        _marker: PhantomData,
+    });
+    take_f32(0.1);
+
+    //call_return_u128_pair();
+
+    let slice = &[0, 1] as &[i32];
+    let slice_ptr = slice as *const [i32] as *const i32;
+
+    assert_eq!(slice_ptr as usize % 4, 0);
+
+    //return;
+
+    unsafe {
+        printf("Hello %s\n\0" as *const str as *const i8, "printf\0" as *const str as *const i8);
+
+        let hello: &[u8] = b"Hello\0" as &[u8; 6];
+        let ptr: *const u8 = hello as *const [u8] as *const u8;
+        puts(ptr);
+
+        let world: Box<&str> = box "World!\0";
+        puts(*world as *const str as *const u8);
+        world as Box<dyn SomeTrait>;
+
+        assert_eq!(intrinsics::bitreverse(0b10101000u8), 0b00010101u8);
+
+        assert_eq!(intrinsics::bswap(0xabu8), 0xabu8);
+        assert_eq!(intrinsics::bswap(0xddccu16), 0xccddu16);
+        assert_eq!(intrinsics::bswap(0xffee_ddccu32), 0xccdd_eeffu32);
+        assert_eq!(intrinsics::bswap(0x1234_5678_ffee_ddccu64), 0xccdd_eeff_7856_3412u64);
+
+        assert_eq!(intrinsics::size_of_val(hello) as u8, 6);
+
+        let chars = &['C', 'h', 'a', 'r', 's'];
+        let chars = chars as &[char];
+        assert_eq!(intrinsics::size_of_val(chars) as u8, 4 * 5);
+
+        let a: &dyn SomeTrait = &"abc\0";
+        a.object_safe();
+
+        assert_eq!(intrinsics::size_of_val(a) as u8, 16);
+        assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4);
+
+        assert_eq!(intrinsics::min_align_of::<u16>() as u8, 2);
+        assert_eq!(intrinsics::min_align_of_val(&a) as u8, intrinsics::min_align_of::<&str>() as u8);
+
+        assert!(!intrinsics::needs_drop::<u8>());
+        assert!(intrinsics::needs_drop::<NoisyDrop>());
+
+        Unique {
+            pointer: 0 as *const &str,
+            _marker: PhantomData,
+        } as Unique<dyn SomeTrait>;
+
+        struct MyDst<T: ?Sized>(T);
+
+        intrinsics::size_of_val(&MyDst([0u8; 4]) as &MyDst<[u8]>);
+
+        struct Foo {
+            x: u8,
+            y: !,
+        }
+
+        unsafe fn uninitialized<T>() -> T {
+            MaybeUninit { uninit: () }.value.value
+        }
+
+        zeroed::<(u8, u8)>();
+        #[allow(unreachable_code)]
+        {
+            if false {
+                zeroed::<!>();
+                zeroed::<Foo>();
+                uninitialized::<Foo>();
+            }
+        }
+    }
+
+    let _ = box NoisyDrop {
+        text: "Boxed outer got dropped!\0",
+        inner: NoisyDropInner,
+    } as Box<dyn SomeTrait>;
+
+    const FUNC_REF: Option<fn()> = Some(main);
+    match FUNC_REF {
+        Some(_) => {},
+        None => assert!(false),
+    }
+
+    match Ordering::Less {
+        Ordering::Less => {},
+        _ => assert!(false),
+    }
+
+    [NoisyDropInner, NoisyDropInner];
+
+    let x = &[0u32, 42u32] as &[u32];
+    match x {
+        [] => assert_eq!(0u32, 1),
+        [_, ref y @ ..] => assert_eq!(&x[1] as *const u32 as usize, &y[0] as *const u32 as usize),
+    }
+
+    assert_eq!(((|()| 42u8) as fn(()) -> u8)(()), 42);
+
+    extern {
+        #[linkage = "weak"]
+        static ABC: *const u8;
+    }
+
+    {
+        extern {
+            #[linkage = "weak"]
+            static ABC: *const u8;
+        }
+    }
+
+    // TODO(antoyo): to make this work, support weak linkage.
+    //unsafe { assert_eq!(ABC as usize, 0); }
+
+    &mut (|| Some(0 as *const ())) as &mut dyn FnMut() -> Option<*const ()>;
+
+    let f = 1000.0;
+    assert_eq!(f as u8, 255);
+    let f2 = -1000.0;
+    assert_eq!(f2 as i8, -128);
+    assert_eq!(f2 as u8, 0);
+
+    static ANOTHER_STATIC: &u8 = &A_STATIC;
+    assert_eq!(*ANOTHER_STATIC, 42);
+
+    check_niche_behavior();
+
+    extern "C" {
+        type ExternType;
+    }
+
+    struct ExternTypeWrapper {
+        _a: ExternType,
+    }
+
+    let nullptr = 0 as *const ();
+    let extern_nullptr = nullptr as *const ExternTypeWrapper;
+    extern_nullptr as *const ();
+    let slice_ptr = &[] as *const [u8];
+    slice_ptr as *const u8;
+
+    #[cfg(not(jit))]
+    test_tls();
+}
+
+#[repr(C)]
+enum c_void {
+    _1,
+    _2,
+}
+
+type c_int = i32;
+type c_ulong = u64;
+
+type pthread_t = c_ulong;
+
+#[repr(C)]
+struct pthread_attr_t {
+    __size: [u64; 7],
+}
+
+#[link(name = "pthread")]
+extern "C" {
+    fn pthread_attr_init(attr: *mut pthread_attr_t) -> c_int;
+
+    fn pthread_create(
+        native: *mut pthread_t,
+        attr: *const pthread_attr_t,
+        f: extern "C" fn(_: *mut c_void) -> *mut c_void,
+        value: *mut c_void
+    ) -> c_int;
+
+    fn pthread_join(
+        native: pthread_t,
+        value: *mut *mut c_void
+    ) -> c_int;
+}
+
+#[thread_local]
+#[cfg(not(jit))]
+static mut TLS: u8 = 42;
+
+#[cfg(not(jit))]
+extern "C" fn mutate_tls(_: *mut c_void) -> *mut c_void {
+    unsafe { TLS = 0; }
+    0 as *mut c_void
+}
+
+#[cfg(not(jit))]
+fn test_tls() {
+    unsafe {
+        let mut attr: pthread_attr_t = zeroed();
+        let mut thread: pthread_t = 0;
+
+        assert_eq!(TLS, 42);
+
+        if pthread_attr_init(&mut attr) != 0 {
+            assert!(false);
+        }
+
+        if pthread_create(&mut thread, &attr, mutate_tls, 0 as *mut c_void) != 0 {
+            assert!(false);
+        }
+
+        let mut res = 0 as *mut c_void;
+        pthread_join(thread, &mut res);
+
+        // TLS of main thread must not have been changed by the other thread.
+        assert_eq!(TLS, 42);
+
+        puts("TLS works!\n\0" as *const str as *const u8);
+    }
+}
+
+// Copied ui/issues/issue-61696.rs
+
+pub enum Infallible {}
+
+// The check that the `bool` field of `V1` is encoding a "niche variant"
+// (i.e. not `V1`, so `V3` or `V4`) used to be mathematically incorrect,
+// causing valid `V1` values to be interpreted as other variants.
+pub enum E1 {
+    V1 { f: bool },
+    V2 { f: Infallible },
+    V3,
+    V4,
+}
+
+// Computing the discriminant used to be done using the niche type (here `u8`,
+// from the `bool` field of `V1`), overflowing for variants with large enough
+// indices (`V3` and `V4`), causing them to be interpreted as other variants.
+pub enum E2<X> {
+    V1 { f: bool },
+
+    /*_00*/ _01(X), _02(X), _03(X), _04(X), _05(X), _06(X), _07(X),
+    _08(X), _09(X), _0A(X), _0B(X), _0C(X), _0D(X), _0E(X), _0F(X),
+    _10(X), _11(X), _12(X), _13(X), _14(X), _15(X), _16(X), _17(X),
+    _18(X), _19(X), _1A(X), _1B(X), _1C(X), _1D(X), _1E(X), _1F(X),
+    _20(X), _21(X), _22(X), _23(X), _24(X), _25(X), _26(X), _27(X),
+    _28(X), _29(X), _2A(X), _2B(X), _2C(X), _2D(X), _2E(X), _2F(X),
+    _30(X), _31(X), _32(X), _33(X), _34(X), _35(X), _36(X), _37(X),
+    _38(X), _39(X), _3A(X), _3B(X), _3C(X), _3D(X), _3E(X), _3F(X),
+    _40(X), _41(X), _42(X), _43(X), _44(X), _45(X), _46(X), _47(X),
+    _48(X), _49(X), _4A(X), _4B(X), _4C(X), _4D(X), _4E(X), _4F(X),
+    _50(X), _51(X), _52(X), _53(X), _54(X), _55(X), _56(X), _57(X),
+    _58(X), _59(X), _5A(X), _5B(X), _5C(X), _5D(X), _5E(X), _5F(X),
+    _60(X), _61(X), _62(X), _63(X), _64(X), _65(X), _66(X), _67(X),
+    _68(X), _69(X), _6A(X), _6B(X), _6C(X), _6D(X), _6E(X), _6F(X),
+    _70(X), _71(X), _72(X), _73(X), _74(X), _75(X), _76(X), _77(X),
+    _78(X), _79(X), _7A(X), _7B(X), _7C(X), _7D(X), _7E(X), _7F(X),
+    _80(X), _81(X), _82(X), _83(X), _84(X), _85(X), _86(X), _87(X),
+    _88(X), _89(X), _8A(X), _8B(X), _8C(X), _8D(X), _8E(X), _8F(X),
+    _90(X), _91(X), _92(X), _93(X), _94(X), _95(X), _96(X), _97(X),
+    _98(X), _99(X), _9A(X), _9B(X), _9C(X), _9D(X), _9E(X), _9F(X),
+    _A0(X), _A1(X), _A2(X), _A3(X), _A4(X), _A5(X), _A6(X), _A7(X),
+    _A8(X), _A9(X), _AA(X), _AB(X), _AC(X), _AD(X), _AE(X), _AF(X),
+    _B0(X), _B1(X), _B2(X), _B3(X), _B4(X), _B5(X), _B6(X), _B7(X),
+    _B8(X), _B9(X), _BA(X), _BB(X), _BC(X), _BD(X), _BE(X), _BF(X),
+    _C0(X), _C1(X), _C2(X), _C3(X), _C4(X), _C5(X), _C6(X), _C7(X),
+    _C8(X), _C9(X), _CA(X), _CB(X), _CC(X), _CD(X), _CE(X), _CF(X),
+    _D0(X), _D1(X), _D2(X), _D3(X), _D4(X), _D5(X), _D6(X), _D7(X),
+    _D8(X), _D9(X), _DA(X), _DB(X), _DC(X), _DD(X), _DE(X), _DF(X),
+    _E0(X), _E1(X), _E2(X), _E3(X), _E4(X), _E5(X), _E6(X), _E7(X),
+    _E8(X), _E9(X), _EA(X), _EB(X), _EC(X), _ED(X), _EE(X), _EF(X),
+    _F0(X), _F1(X), _F2(X), _F3(X), _F4(X), _F5(X), _F6(X), _F7(X),
+    _F8(X), _F9(X), _FA(X), _FB(X), _FC(X), _FD(X), _FE(X), _FF(X),
+
+    V3,
+    V4,
+}
+
+fn check_niche_behavior () {
+    if let E1::V2 { .. } = (E1::V1 { f: true }) {
+        intrinsics::abort();
+    }
+
+    if let E2::V1 { .. } = E2::V3::<Infallible> {
+        intrinsics::abort();
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/example/mod_bench.rs b/compiler/rustc_codegen_gcc/example/mod_bench.rs
new file mode 100644 (file)
index 0000000..2e2b005
--- /dev/null
@@ -0,0 +1,37 @@
+#![feature(start, box_syntax, core_intrinsics, lang_items)]
+#![no_std]
+
+#[link(name = "c")]
+extern {}
+
+#[panic_handler]
+fn panic_handler(_: &core::panic::PanicInfo) -> ! {
+    unsafe {
+        core::intrinsics::abort();
+    }
+}
+
+#[lang="eh_personality"]
+fn eh_personality(){}
+
+// Required for rustc_codegen_llvm
+#[no_mangle]
+unsafe extern "C" fn _Unwind_Resume() {
+    core::intrinsics::unreachable();
+}
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+    for i in 2..100_000_000 {
+        black_box((i + 1) % i);
+    }
+
+    0
+}
+
+#[inline(never)]
+fn black_box(i: u32) {
+    if i != 1 {
+        unsafe { core::intrinsics::abort(); }
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/example/std_example.rs b/compiler/rustc_codegen_gcc/example/std_example.rs
new file mode 100644 (file)
index 0000000..eba0eb8
--- /dev/null
@@ -0,0 +1,278 @@
+#![feature(core_intrinsics, generators, generator_trait, is_sorted)]
+
+use std::arch::x86_64::*;
+use std::io::Write;
+use std::ops::Generator;
+
+extern {
+    pub fn printf(format: *const i8, ...) -> i32;
+}
+
+fn main() {
+    let mutex = std::sync::Mutex::new(());
+    let _guard = mutex.lock().unwrap();
+
+    let _ = ::std::iter::repeat('a' as u8).take(10).collect::<Vec<_>>();
+    let stderr = ::std::io::stderr();
+    let mut stderr = stderr.lock();
+
+    std::thread::spawn(move || {
+        println!("Hello from another thread!");
+    });
+
+    writeln!(stderr, "some {} text", "<unknown>").unwrap();
+
+    let _ = std::process::Command::new("true").env("c", "d").spawn();
+
+    println!("cargo:rustc-link-lib=z");
+
+    static ONCE: std::sync::Once = std::sync::Once::new();
+    ONCE.call_once(|| {});
+
+    let _eq = LoopState::Continue(()) == LoopState::Break(());
+
+    // Make sure ByValPair values with differently sized components are correctly passed
+    map(None::<(u8, Box<Instruction>)>);
+
+    println!("{}", 2.3f32.exp());
+    println!("{}", 2.3f32.exp2());
+    println!("{}", 2.3f32.abs());
+    println!("{}", 2.3f32.sqrt());
+    println!("{}", 2.3f32.floor());
+    println!("{}", 2.3f32.ceil());
+    println!("{}", 2.3f32.min(1.0));
+    println!("{}", 2.3f32.max(1.0));
+    println!("{}", 2.3f32.powi(2));
+    println!("{}", 2.3f32.log2());
+    assert_eq!(2.3f32.copysign(-1.0), -2.3f32);
+    println!("{}", 2.3f32.powf(2.0));
+
+    assert_eq!(-128i8, (-128i8).saturating_sub(1));
+    assert_eq!(127i8, 127i8.saturating_sub(-128));
+    assert_eq!(-128i8, (-128i8).saturating_add(-128));
+    assert_eq!(127i8, 127i8.saturating_add(1));
+
+    assert_eq!(-32768i16, (-32768i16).saturating_add(-32768));
+    assert_eq!(32767i16, 32767i16.saturating_add(1));
+
+    assert_eq!(0b0000000000000000000000000010000010000000000000000000000000000000_0000000000100000000000000000000000001000000000000100000000000000u128.leading_zeros(), 26);
+    assert_eq!(0b0000000000000000000000000010000000000000000000000000000000000000_0000000000000000000000000000000000001000000000000000000010000000u128.trailing_zeros(), 7);
+
+    let _d = 0i128.checked_div(2i128);
+    let _d = 0u128.checked_div(2u128);
+    assert_eq!(1u128 + 2, 3);
+
+    assert_eq!(0b100010000000000000000000000000000u128 >> 10, 0b10001000000000000000000u128);
+    assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 >> 64, 0xFEDCBA98765432u128);
+    assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 as i128 >> 64, 0xFEDCBA98765432i128);
+
+    let tmp = 353985398u128;
+    assert_eq!(tmp * 932490u128, 330087843781020u128);
+
+    let tmp = -0x1234_5678_9ABC_DEF0i64;
+    assert_eq!(tmp as i128, -0x1234_5678_9ABC_DEF0i128);
+
+    // Check that all u/i128 <-> float casts work correctly.
+    let houndred_u128 = 100u128;
+    let houndred_i128 = 100i128;
+    let houndred_f32 = 100.0f32;
+    let houndred_f64 = 100.0f64;
+    assert_eq!(houndred_u128 as f32, 100.0);
+    assert_eq!(houndred_u128 as f64, 100.0);
+    assert_eq!(houndred_f32 as u128, 100);
+    assert_eq!(houndred_f64 as u128, 100);
+    assert_eq!(houndred_i128 as f32, 100.0);
+    assert_eq!(houndred_i128 as f64, 100.0);
+    assert_eq!(houndred_f32 as i128, 100);
+    assert_eq!(houndred_f64 as i128, 100);
+
+    let _a = 1u32 << 2u8;
+
+    let empty: [i32; 0] = [];
+    assert!(empty.is_sorted());
+
+    println!("{:?}", std::intrinsics::caller_location());
+
+    /*unsafe {
+        test_simd();
+    }*/
+
+    Box::pin(move |mut _task_context| {
+        yield ();
+    }).as_mut().resume(0);
+
+    println!("End");
+}
+
+/*#[target_feature(enable = "sse2")]
+unsafe fn test_simd() {
+    let x = _mm_setzero_si128();
+    let y = _mm_set1_epi16(7);
+    let or = _mm_or_si128(x, y);
+    let cmp_eq = _mm_cmpeq_epi8(y, y);
+    let cmp_lt = _mm_cmplt_epi8(y, y);
+
+    /*assert_eq!(std::mem::transmute::<_, [u16; 8]>(or), [7, 7, 7, 7, 7, 7, 7, 7]);
+    assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_eq), [0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff]);
+    assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_lt), [0, 0, 0, 0, 0, 0, 0, 0]);
+
+    test_mm_slli_si128();
+    test_mm_movemask_epi8();
+    test_mm256_movemask_epi8();
+    test_mm_add_epi8();
+    test_mm_add_pd();
+    test_mm_cvtepi8_epi16();
+    test_mm_cvtsi128_si64();
+
+    // FIXME(#666) implement `#[rustc_arg_required_const(..)]` support
+    //test_mm_extract_epi8();
+
+    let mask1 = _mm_movemask_epi8(dbg!(_mm_setr_epi8(255u8 as i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)));
+    assert_eq!(mask1, 1);*/
+}*/
+
+/*#[target_feature(enable = "sse2")]
+unsafe fn test_mm_slli_si128() {
+    #[rustfmt::skip]
+    let a = _mm_setr_epi8(
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+    );
+    let r = _mm_slli_si128(a, 1);
+    let e = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
+    assert_eq_m128i(r, e);
+
+    #[rustfmt::skip]
+    let a = _mm_setr_epi8(
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+    );
+    let r = _mm_slli_si128(a, 15);
+    let e = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+    assert_eq_m128i(r, e);
+
+    #[rustfmt::skip]
+    let a = _mm_setr_epi8(
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+    );
+    let r = _mm_slli_si128(a, 16);
+    assert_eq_m128i(r, _mm_set1_epi8(0));
+
+    #[rustfmt::skip]
+    let a = _mm_setr_epi8(
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+    );
+    let r = _mm_slli_si128(a, -1);
+    assert_eq_m128i(_mm_set1_epi8(0), r);
+
+    #[rustfmt::skip]
+    let a = _mm_setr_epi8(
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+    );
+    let r = _mm_slli_si128(a, -0x80000000);
+    assert_eq_m128i(r, _mm_set1_epi8(0));
+}
+
+#[target_feature(enable = "sse2")]
+unsafe fn test_mm_movemask_epi8() {
+    #[rustfmt::skip]
+    let a = _mm_setr_epi8(
+        0b1000_0000u8 as i8, 0b0, 0b1000_0000u8 as i8, 0b01,
+        0b0101, 0b1111_0000u8 as i8, 0, 0,
+        0, 0, 0b1111_0000u8 as i8, 0b0101,
+        0b01, 0b1000_0000u8 as i8, 0b0, 0b1000_0000u8 as i8,
+    );
+    let r = _mm_movemask_epi8(a);
+    assert_eq!(r, 0b10100100_00100101);
+}
+
+#[target_feature(enable = "avx2")]
+unsafe fn test_mm256_movemask_epi8() {
+    let a = _mm256_set1_epi8(-1);
+    let r = _mm256_movemask_epi8(a);
+    let e = -1;
+    assert_eq!(r, e);
+}
+
+#[target_feature(enable = "sse2")]
+unsafe fn test_mm_add_epi8() {
+    let a = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
+    #[rustfmt::skip]
+    let b = _mm_setr_epi8(
+        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+    );
+    let r = _mm_add_epi8(a, b);
+    #[rustfmt::skip]
+    let e = _mm_setr_epi8(
+        16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
+    );
+    assert_eq_m128i(r, e);
+}
+
+#[target_feature(enable = "sse2")]
+unsafe fn test_mm_add_pd() {
+    let a = _mm_setr_pd(1.0, 2.0);
+    let b = _mm_setr_pd(5.0, 10.0);
+    let r = _mm_add_pd(a, b);
+    assert_eq_m128d(r, _mm_setr_pd(6.0, 12.0));
+}
+
+fn assert_eq_m128i(x: std::arch::x86_64::__m128i, y: std::arch::x86_64::__m128i) {
+    unsafe {
+        assert_eq!(std::mem::transmute::<_, [u8; 16]>(x), std::mem::transmute::<_, [u8; 16]>(y));
+    }
+}
+
+#[target_feature(enable = "sse2")]
+pub unsafe fn assert_eq_m128d(a: __m128d, b: __m128d) {
+    if _mm_movemask_pd(_mm_cmpeq_pd(a, b)) != 0b11 {
+        panic!("{:?} != {:?}", a, b);
+    }
+}
+
+#[target_feature(enable = "sse2")]
+unsafe fn test_mm_cvtsi128_si64() {
+    let r = _mm_cvtsi128_si64(std::mem::transmute::<[i64; 2], _>([5, 0]));
+    assert_eq!(r, 5);
+}
+
+#[target_feature(enable = "sse4.1")]
+unsafe fn test_mm_cvtepi8_epi16() {
+    let a = _mm_set1_epi8(10);
+    let r = _mm_cvtepi8_epi16(a);
+    let e = _mm_set1_epi16(10);
+    assert_eq_m128i(r, e);
+    let a = _mm_set1_epi8(-10);
+    let r = _mm_cvtepi8_epi16(a);
+    let e = _mm_set1_epi16(-10);
+    assert_eq_m128i(r, e);
+}
+
+#[target_feature(enable = "sse4.1")]
+unsafe fn test_mm_extract_epi8() {
+    #[rustfmt::skip]
+    let a = _mm_setr_epi8(
+        -1, 1, 2, 3, 4, 5, 6, 7,
+        8, 9, 10, 11, 12, 13, 14, 15
+    );
+    let r1 = _mm_extract_epi8(a, 0);
+    let r2 = _mm_extract_epi8(a, 19);
+    assert_eq!(r1, 0xFF);
+    assert_eq!(r2, 3);
+}*/
+
+#[derive(PartialEq)]
+enum LoopState {
+    Continue(()),
+    Break(())
+}
+
+pub enum Instruction {
+    Increment,
+    Loop,
+}
+
+fn map(a: Option<(u8, Box<Instruction>)>) -> Option<Box<Instruction>> {
+    match a {
+        None => None,
+        Some((_, instr)) => Some(instr),
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/example/subslice-patterns-const-eval.rs b/compiler/rustc_codegen_gcc/example/subslice-patterns-const-eval.rs
new file mode 100644 (file)
index 0000000..2cb8478
--- /dev/null
@@ -0,0 +1,97 @@
+// Based on https://github.com/rust-lang/rust/blob/c5840f9d252c2f5cc16698dbf385a29c5de3ca07/src/test/ui/array-slice-vec/subslice-patterns-const-eval-match.rs
+
+// Test that array subslice patterns are correctly handled in const evaluation.
+
+// run-pass
+
+#[derive(PartialEq, Debug, Clone)]
+struct N(u8);
+
+#[derive(PartialEq, Debug, Clone)]
+struct Z;
+
+macro_rules! n {
+    ($($e:expr),* $(,)?) => {
+        [$(N($e)),*]
+    }
+}
+
+// This macro has an unused variable so that it can be repeated base on the
+// number of times a repeated variable (`$e` in `z`) occurs.
+macro_rules! zed {
+    ($e:expr) => { Z }
+}
+
+macro_rules! z {
+    ($($e:expr),* $(,)?) => {
+        [$(zed!($e)),*]
+    }
+}
+
+// Compare constant evaluation and runtime evaluation of a given expression.
+macro_rules! compare_evaluation {
+    ($e:expr, $t:ty $(,)?) => {{
+        const CONST_EVAL: $t = $e;
+        const fn const_eval() -> $t { $e }
+        static CONST_EVAL2: $t = const_eval();
+        let runtime_eval = $e;
+        assert_eq!(CONST_EVAL, runtime_eval);
+        assert_eq!(CONST_EVAL2, runtime_eval);
+    }}
+}
+
+// Repeat `$test`, substituting the given macro variables with the given
+// identifiers.
+//
+// For example:
+//
+// repeat! {
+//     ($name); X; Y:
+//     struct $name;
+// }
+//
+// Expands to:
+//
+// struct X; struct Y;
+//
+// This is used to repeat the tests using both the `N` and `Z`
+// types.
+macro_rules! repeat {
+    (($($dollar:tt $placeholder:ident)*); $($($values:ident),+);*: $($test:tt)*) => {
+        macro_rules! single {
+            ($($dollar $placeholder:ident),*) => { $($test)* }
+        }
+        $(single!($($values),+);)*
+    }
+}
+
+fn main() {
+    repeat! {
+        ($arr $Ty); n, N; z, Z:
+        compare_evaluation!({ let [_, x @ .., _] = $arr!(1, 2, 3, 4); x }, [$Ty; 2]);
+        compare_evaluation!({ let [_, ref x @ .., _] = $arr!(1, 2, 3, 4); x }, &'static [$Ty; 2]);
+        compare_evaluation!({ let [_, x @ .., _] = &$arr!(1, 2, 3, 4); x }, &'static [$Ty; 2]);
+
+        compare_evaluation!({ let [_, _, x @ .., _, _] = $arr!(1, 2, 3, 4); x }, [$Ty; 0]);
+        compare_evaluation!(
+            { let [_, _, ref x @ .., _, _] = $arr!(1, 2, 3, 4); x },
+            &'static [$Ty; 0],
+        );
+        compare_evaluation!(
+            { let [_, _, x @ .., _, _] = &$arr!(1, 2, 3, 4); x },
+            &'static [$Ty; 0],
+        );
+
+        compare_evaluation!({ let [_, .., x] = $arr!(1, 2, 3, 4); x }, $Ty);
+        compare_evaluation!({ let [_, .., ref x] = $arr!(1, 2, 3, 4); x }, &'static $Ty);
+        compare_evaluation!({ let [_, _y @ .., x] = &$arr!(1, 2, 3, 4); x }, &'static $Ty);
+    }
+
+    compare_evaluation!({ let [_, .., N(x)] = n!(1, 2, 3, 4); x }, u8);
+    compare_evaluation!({ let [_, .., N(ref x)] = n!(1, 2, 3, 4); x }, &'static u8);
+    compare_evaluation!({ let [_, .., N(x)] = &n!(1, 2, 3, 4); x }, &'static u8);
+
+    compare_evaluation!({ let [N(x), .., _] = n!(1, 2, 3, 4); x }, u8);
+    compare_evaluation!({ let [N(ref x), .., _] = n!(1, 2, 3, 4); x }, &'static u8);
+    compare_evaluation!({ let [N(x), .., _] = &n!(1, 2, 3, 4); x }, &'static u8);
+}
diff --git a/compiler/rustc_codegen_gcc/example/track-caller-attribute.rs b/compiler/rustc_codegen_gcc/example/track-caller-attribute.rs
new file mode 100644 (file)
index 0000000..93bab17
--- /dev/null
@@ -0,0 +1,40 @@
+// Based on https://github.com/anp/rust/blob/175631311716d7dfeceec40d2587cde7142ffa8c/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs
+
+// run-pass
+
+use std::panic::Location;
+
+#[track_caller]
+fn tracked() -> &'static Location<'static> {
+    Location::caller()
+}
+
+fn nested_intrinsic() -> &'static Location<'static> {
+    Location::caller()
+}
+
+fn nested_tracked() -> &'static Location<'static> {
+    tracked()
+}
+
+fn main() {
+    let location = Location::caller();
+    assert_eq!(location.file(), file!());
+    assert_eq!(location.line(), 21);
+    assert_eq!(location.column(), 20);
+
+    let tracked = tracked();
+    assert_eq!(tracked.file(), file!());
+    assert_eq!(tracked.line(), 26);
+    assert_eq!(tracked.column(), 19);
+
+    let nested = nested_intrinsic();
+    assert_eq!(nested.file(), file!());
+    assert_eq!(nested.line(), 13);
+    assert_eq!(nested.column(), 5);
+
+    let contained = nested_tracked();
+    assert_eq!(contained.file(), file!());
+    assert_eq!(contained.line(), 17);
+    assert_eq!(contained.column(), 5);
+}
diff --git a/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch
new file mode 100644 (file)
index 0000000..aae62a9
--- /dev/null
@@ -0,0 +1,63 @@
+From f6befc4bb51d84f5f1cf35938a168c953d421350 Mon Sep 17 00:00:00 2001
+From: bjorn3 <bjorn3@users.noreply.github.com>
+Date: Sun, 24 Nov 2019 15:10:23 +0100
+Subject: [PATCH] [core] Disable not compiling tests
+
+---
+ library/core/tests/Cargo.toml         | 8 ++++++++
+ library/core/tests/num/flt2dec/mod.rs | 1 -
+ library/core/tests/num/int_macros.rs  | 2 ++
+ library/core/tests/num/uint_macros.rs | 2 ++
+ library/core/tests/ptr.rs             | 2 ++
+ library/core/tests/slice.rs           | 2 ++
+ 6 files changed, 16 insertions(+), 1 deletion(-)
+ create mode 100644 library/core/tests/Cargo.toml
+
+diff --git a/library/core/tests/Cargo.toml b/library/core/tests/Cargo.toml
+new file mode 100644
+index 0000000..46fd999
+--- /dev/null
++++ b/library/core/tests/Cargo.toml
+@@ -0,0 +1,8 @@
++[package]
++name = "core"
++version = "0.0.0"
++edition = "2018"
++
++[lib]
++name = "coretests"
++path = "lib.rs"
+diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs
+index a35897e..f0bf645 100644
+--- a/library/core/tests/num/flt2dec/mod.rs
++++ b/library/core/tests/num/flt2dec/mod.rs
+@@ -13,7 +13,6 @@ mod strategy {
+     mod dragon;
+     mod grisu;
+ }
+-mod random;
+ pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
+     match decode(v).1 {
+diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
+index 6609bc3..241b497 100644
+--- a/library/core/tests/slice.rs
++++ b/library/core/tests/slice.rs
+@@ -1209,6 +1209,7 @@ fn brute_force_rotate_test_1() {
+     }
+ }
++/*
+ #[test]
+ #[cfg(not(target_arch = "wasm32"))]
+ fn sort_unstable() {
+@@ -1394,6 +1395,7 @@ fn partition_at_index() {
+     v.select_nth_unstable(0);
+     assert!(v == [0xDEADBEEF]);
+ }
++*/
+ #[test]
+ #[should_panic(expected = "index 0 greater than length of slice")]
+--
+2.21.0 (Apple Git-122)
diff --git a/compiler/rustc_codegen_gcc/patches/0023-core-Ignore-failing-tests.patch b/compiler/rustc_codegen_gcc/patches/0023-core-Ignore-failing-tests.patch
new file mode 100644 (file)
index 0000000..ee5ba44
--- /dev/null
@@ -0,0 +1,49 @@
+From dd82e95c9de212524e14fc60155de1ae40156dfc Mon Sep 17 00:00:00 2001
+From: bjorn3 <bjorn3@users.noreply.github.com>
+Date: Sun, 24 Nov 2019 15:34:06 +0100
+Subject: [PATCH] [core] Ignore failing tests
+
+---
+ library/core/tests/iter.rs       |  4 ++++
+ library/core/tests/num/bignum.rs | 10 ++++++++++
+ library/core/tests/num/mod.rs    |  5 +++--
+ library/core/tests/time.rs       |  1 +
+ 4 files changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs
+index 4bc44e9..8e3c7a4 100644
+--- a/library/core/tests/array.rs
++++ b/library/core/tests/array.rs
+@@ -242,6 +242,7 @@ fn iterator_drops() {
+     assert_eq!(i.get(), 5);
+ }
++/*
+ // This test does not work on targets without panic=unwind support.
+ // To work around this problem, test is marked is should_panic, so it will
+ // be automagically skipped on unsuitable targets, such as
+@@ -283,6 +284,7 @@ fn array_default_impl_avoids_leaks_on_panic() {
+     assert_eq!(COUNTER.load(Relaxed), 0);
+     panic!("test succeeded")
+ }
++*/
+ #[test]
+ fn empty_array_is_always_default() {
+@@ -304,6 +304,7 @@ fn array_map() {
+     assert_eq!(b, [1, 2, 3]);
+ }
++/*
+ // See note on above test for why `should_panic` is used.
+ #[test]
+ #[should_panic(expected = "test succeeded")]
+@@ -332,6 +333,7 @@ fn array_map_drop_safety() {
+     assert_eq!(DROPPED.load(Ordering::SeqCst), num_to_create);
+     panic!("test succeeded")
+ }
++*/
+ #[test]
+ fn cell_allows_array_cycle() {
+-- 2.21.0 (Apple Git-122)
diff --git a/compiler/rustc_codegen_gcc/prepare.sh b/compiler/rustc_codegen_gcc/prepare.sh
new file mode 100755 (executable)
index 0000000..503fa29
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/bash --verbose
+set -e
+
+source prepare_build.sh
+
+cargo install hyperfine || echo "Skipping hyperfine install"
+
+git clone https://github.com/rust-lang/regex.git || echo "rust-lang/regex has already been cloned"
+pushd regex
+git checkout -- .
+git checkout 341f207c1071f7290e3f228c710817c280c8dca1
+popd
+
+git clone https://github.com/ebobby/simple-raytracer || echo "ebobby/simple-raytracer has already been cloned"
+pushd simple-raytracer
+git checkout -- .
+git checkout 804a7a21b9e673a482797aa289a18ed480e4d813
+
+# build with cg_llvm for perf comparison
+cargo build
+mv target/debug/main raytracer_cg_llvm
+popd
diff --git a/compiler/rustc_codegen_gcc/prepare_build.sh b/compiler/rustc_codegen_gcc/prepare_build.sh
new file mode 100755 (executable)
index 0000000..ccf5350
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/bash --verbose
+set -e
+
+rustup component add rust-src rustc-dev llvm-tools-preview
+./build_sysroot/prepare_sysroot_src.sh
diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain
new file mode 100644 (file)
index 0000000..d311a33
--- /dev/null
@@ -0,0 +1 @@
+nightly-2021-09-28
diff --git a/compiler/rustc_codegen_gcc/rustup.sh b/compiler/rustc_codegen_gcc/rustup.sh
new file mode 100755 (executable)
index 0000000..01ce5bb
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+set -e
+
+case $1 in
+    "prepare")
+        TOOLCHAIN=$(date +%Y-%m-%d)
+
+        echo "=> Installing new nightly"
+        rustup toolchain install --profile minimal nightly-${TOOLCHAIN} # Sanity check to see if the nightly exists
+        echo nightly-${TOOLCHAIN} > rust-toolchain
+
+        echo "=> Uninstalling all old nighlies"
+        for nightly in $(rustup toolchain list | grep nightly | grep -v $TOOLCHAIN | grep -v nightly-x86_64); do
+            rustup toolchain uninstall $nightly
+        done
+
+        ./clean_all.sh
+        ./prepare.sh
+        ;;
+    "commit")
+        git add rust-toolchain
+        git commit -m "Rustup to $(rustc -V)"
+        ;;
+    *)
+        echo "Unknown command '$1'"
+        echo "Usage: ./rustup.sh prepare|commit"
+        ;;
+esac
diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs
new file mode 100644 (file)
index 0000000..ce428c5
--- /dev/null
@@ -0,0 +1,160 @@
+use gccjit::{ToRValue, Type};
+use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods};
+use rustc_middle::bug;
+use rustc_middle::ty::Ty;
+use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind};
+
+use crate::builder::Builder;
+use crate::context::CodegenCx;
+use crate::intrinsic::ArgAbiExt;
+use crate::type_of::LayoutGccExt;
+
+impl<'a, 'gcc, 'tcx> AbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
+    fn apply_attrs_callsite(&mut self, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _callsite: Self::Value) {
+        // TODO(antoyo)
+    }
+
+    fn get_param(&self, index: usize) -> Self::Value {
+        self.cx.current_func.borrow().expect("current func")
+            .get_param(index as i32)
+            .to_rvalue()
+    }
+}
+
+impl GccType for CastTarget {
+    fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, '_>) -> Type<'gcc> {
+        let rest_gcc_unit = self.rest.unit.gcc_type(cx);
+        let (rest_count, rem_bytes) =
+            if self.rest.unit.size.bytes() == 0 {
+                (0, 0)
+            }
+            else {
+                (self.rest.total.bytes() / self.rest.unit.size.bytes(), self.rest.total.bytes() % self.rest.unit.size.bytes())
+            };
+
+        if self.prefix.iter().all(|x| x.is_none()) {
+            // Simplify to a single unit when there is no prefix and size <= unit size
+            if self.rest.total <= self.rest.unit.size {
+                return rest_gcc_unit;
+            }
+
+            // Simplify to array when all chunks are the same size and type
+            if rem_bytes == 0 {
+                return cx.type_array(rest_gcc_unit, rest_count);
+            }
+        }
+
+        // Create list of fields in the main structure
+        let mut args: Vec<_> = self
+            .prefix
+            .iter()
+            .flat_map(|option_kind| {
+                option_kind.map(|kind| Reg { kind, size: self.prefix_chunk_size }.gcc_type(cx))
+            })
+            .chain((0..rest_count).map(|_| rest_gcc_unit))
+            .collect();
+
+        // Append final integer
+        if rem_bytes != 0 {
+            // Only integers can be really split further.
+            assert_eq!(self.rest.unit.kind, RegKind::Integer);
+            args.push(cx.type_ix(rem_bytes * 8));
+        }
+
+        cx.type_struct(&args, false)
+    }
+}
+
+pub trait GccType {
+    fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, '_>) -> Type<'gcc>;
+}
+
+impl GccType for Reg {
+    fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, '_>) -> Type<'gcc> {
+        match self.kind {
+            RegKind::Integer => cx.type_ix(self.size.bits()),
+            RegKind::Float => {
+                match self.size.bits() {
+                    32 => cx.type_f32(),
+                    64 => cx.type_f64(),
+                    _ => bug!("unsupported float: {:?}", self),
+                }
+            },
+            RegKind::Vector => unimplemented!(), //cx.type_vector(cx.type_i8(), self.size.bytes()),
+        }
+    }
+}
+
+pub trait FnAbiGccExt<'gcc, 'tcx> {
+    // TODO(antoyo): return a function pointer type instead?
+    fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec<Type<'gcc>>, bool);
+    fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
+}
+
+impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
+    fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec<Type<'gcc>>, bool) {
+        let args_capacity: usize = self.args.iter().map(|arg|
+            if arg.pad.is_some() {
+                1
+            }
+            else {
+                0
+            } +
+            if let PassMode::Pair(_, _) = arg.mode {
+                2
+            } else {
+                1
+            }
+        ).sum();
+        let mut argument_tys = Vec::with_capacity(
+            if let PassMode::Indirect { .. } = self.ret.mode {
+                1
+            }
+            else {
+                0
+            } + args_capacity,
+        );
+
+        let return_ty =
+            match self.ret.mode {
+                PassMode::Ignore => cx.type_void(),
+                PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_gcc_type(cx),
+                PassMode::Cast(cast) => cast.gcc_type(cx),
+                PassMode::Indirect { .. } => {
+                    argument_tys.push(cx.type_ptr_to(self.ret.memory_ty(cx)));
+                    cx.type_void()
+                }
+            };
+
+        for arg in &self.args {
+            // add padding
+            if let Some(ty) = arg.pad {
+                argument_tys.push(ty.gcc_type(cx));
+            }
+
+            let arg_ty = match arg.mode {
+                PassMode::Ignore => continue,
+                PassMode::Direct(_) => arg.layout.immediate_gcc_type(cx),
+                PassMode::Pair(..) => {
+                    argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 0, true));
+                    argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 1, true));
+                    continue;
+                }
+                PassMode::Indirect { extra_attrs: Some(_), .. } => {
+                    unimplemented!();
+                }
+                PassMode::Cast(cast) => cast.gcc_type(cx),
+                PassMode::Indirect { extra_attrs: None, .. } => cx.type_ptr_to(arg.memory_ty(cx)),
+            };
+            argument_tys.push(arg_ty);
+        }
+
+        (return_ty, argument_tys, self.c_variadic)
+    }
+
+    fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+        let (return_type, params, variadic) = self.gcc_type(cx);
+        let pointer_type = cx.context.new_function_pointer_type(None, return_type, &params, variadic);
+        pointer_type
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs
new file mode 100644 (file)
index 0000000..6378a31
--- /dev/null
@@ -0,0 +1,116 @@
+use gccjit::{FunctionType, ToRValue};
+use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
+use rustc_middle::bug;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::sym;
+
+use crate::GccContext;
+
+pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) {
+    let context = &mods.context;
+    let usize =
+        match tcx.sess.target.pointer_width {
+            16 => context.new_type::<u16>(),
+            32 => context.new_type::<u32>(),
+            64 => context.new_type::<u64>(),
+            tws => bug!("Unsupported target word size for int: {}", tws),
+        };
+    let i8 = context.new_type::<i8>();
+    let i8p = i8.make_pointer();
+    let void = context.new_type::<()>();
+
+    for method in ALLOCATOR_METHODS {
+        let mut types = Vec::with_capacity(method.inputs.len());
+        for ty in method.inputs.iter() {
+            match *ty {
+                AllocatorTy::Layout => {
+                    types.push(usize);
+                    types.push(usize);
+                }
+                AllocatorTy::Ptr => types.push(i8p),
+                AllocatorTy::Usize => types.push(usize),
+
+                AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
+            }
+        }
+        let output = match method.output {
+            AllocatorTy::ResultPtr => Some(i8p),
+            AllocatorTy::Unit => None,
+
+            AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
+                panic!("invalid allocator output")
+            }
+        };
+        let name = format!("__rust_{}", method.name);
+
+        let args: Vec<_> = types.iter().enumerate()
+            .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
+            .collect();
+        let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false);
+
+        if tcx.sess.target.options.default_hidden_visibility {
+            // TODO(antoyo): set visibility.
+        }
+        if tcx.sess.must_emit_unwind_tables() {
+            // TODO(antoyo): emit unwind tables.
+        }
+
+        let callee = kind.fn_name(method.name);
+        let args: Vec<_> = types.iter().enumerate()
+            .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
+            .collect();
+        let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false);
+        // TODO(antoyo): set visibility.
+
+        let block = func.new_block("entry");
+
+        let args = args
+            .iter()
+            .enumerate()
+            .map(|(i, _)| func.get_param(i as i32).to_rvalue())
+            .collect::<Vec<_>>();
+        let ret = context.new_call(None, callee, &args);
+        //llvm::LLVMSetTailCall(ret, True);
+        if output.is_some() {
+            block.end_with_return(None, ret);
+        }
+        else {
+            block.end_with_void_return(None);
+        }
+
+        // TODO(@Commeownist): Check if we need to emit some extra debugging info in certain circumstances
+        // as described in https://github.com/rust-lang/rust/commit/77a96ed5646f7c3ee8897693decc4626fe380643
+    }
+
+    let types = [usize, usize];
+    let name = "__rust_alloc_error_handler".to_string();
+    let args: Vec<_> = types.iter().enumerate()
+        .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
+        .collect();
+    let func = context.new_function(None, FunctionType::Exported, void, &args, name, false);
+
+    let kind =
+        if has_alloc_error_handler {
+            AllocatorKind::Global
+        }
+        else {
+            AllocatorKind::Default
+        };
+    let callee = kind.fn_name(sym::oom);
+    let args: Vec<_> = types.iter().enumerate()
+        .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index)))
+        .collect();
+    let callee = context.new_function(None, FunctionType::Extern, void, &args, callee, false);
+    //llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
+
+    let block = func.new_block("entry");
+
+    let args = args
+        .iter()
+        .enumerate()
+        .map(|(i, _)| func.get_param(i as i32).to_rvalue())
+        .collect::<Vec<_>>();
+    let _ret = context.new_call(None, callee, &args);
+    //llvm::LLVMSetTailCall(ret, True);
+    block.end_with_void_return(None);
+}
diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs
new file mode 100644 (file)
index 0000000..d749d76
--- /dev/null
@@ -0,0 +1,218 @@
+use std::fs::File;
+use std::path::{Path, PathBuf};
+
+use rustc_session::Session;
+use rustc_codegen_ssa::back::archive::ArchiveBuilder;
+
+use rustc_data_structures::temp_dir::MaybeTempDir;
+use rustc_middle::middle::cstore::DllImport;
+
+
+struct ArchiveConfig<'a> {
+    sess: &'a Session,
+    dst: PathBuf,
+    use_native_ar: bool,
+    use_gnu_style_archive: bool,
+}
+
+#[derive(Debug)]
+enum ArchiveEntry {
+    FromArchive {
+        archive_index: usize,
+        entry_index: usize,
+    },
+    File(PathBuf),
+}
+
+pub struct ArArchiveBuilder<'a> {
+    config: ArchiveConfig<'a>,
+    src_archives: Vec<(PathBuf, ar::Archive<File>)>,
+    // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
+    // the end of an archive for linkers to not get confused.
+    entries: Vec<(String, ArchiveEntry)>,
+}
+
+impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
+    fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
+        let config = ArchiveConfig {
+            sess,
+            dst: output.to_path_buf(),
+            use_native_ar: false,
+            // FIXME test for linux and System V derivatives instead
+            use_gnu_style_archive: sess.target.options.archive_format == "gnu",
+        };
+
+        let (src_archives, entries) = if let Some(input) = input {
+            let mut archive = ar::Archive::new(File::open(input).unwrap());
+            let mut entries = Vec::new();
+
+            let mut i = 0;
+            while let Some(entry) = archive.next_entry() {
+                let entry = entry.unwrap();
+                entries.push((
+                    String::from_utf8(entry.header().identifier().to_vec()).unwrap(),
+                    ArchiveEntry::FromArchive {
+                        archive_index: 0,
+                        entry_index: i,
+                    },
+                ));
+                i += 1;
+            }
+
+            (vec![(input.to_owned(), archive)], entries)
+        } else {
+            (vec![], Vec::new())
+        };
+
+        ArArchiveBuilder {
+            config,
+            src_archives,
+            entries,
+        }
+    }
+
+    fn src_files(&mut self) -> Vec<String> {
+        self.entries.iter().map(|(name, _)| name.clone()).collect()
+    }
+
+    fn remove_file(&mut self, name: &str) {
+        let index = self
+            .entries
+            .iter()
+            .position(|(entry_name, _)| entry_name == name)
+            .expect("Tried to remove file not existing in src archive");
+        self.entries.remove(index);
+    }
+
+    fn add_file(&mut self, file: &Path) {
+        self.entries.push((
+            file.file_name().unwrap().to_str().unwrap().to_string(),
+            ArchiveEntry::File(file.to_owned()),
+        ));
+    }
+
+    fn add_archive<F>(&mut self, archive_path: &Path, mut skip: F) -> std::io::Result<()>
+    where
+        F: FnMut(&str) -> bool + 'static,
+    {
+        let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
+        let archive_index = self.src_archives.len();
+
+        let mut i = 0;
+        while let Some(entry) = archive.next_entry() {
+            let entry = entry?;
+            let file_name = String::from_utf8(entry.header().identifier().to_vec())
+                .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
+            if !skip(&file_name) {
+                self.entries
+                    .push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i }));
+            }
+            i += 1;
+        }
+
+        self.src_archives.push((archive_path.to_owned(), archive));
+        Ok(())
+    }
+
+    fn update_symbols(&mut self) {
+    }
+
+    fn build(mut self) {
+        use std::process::Command;
+
+        fn add_file_using_ar(archive: &Path, file: &Path) {
+            Command::new("ar")
+                .arg("r") // add or replace file
+                .arg("-c") // silence created file message
+                .arg(archive)
+                .arg(&file)
+                .status()
+                .unwrap();
+        }
+
+        enum BuilderKind<'a> {
+            Bsd(ar::Builder<File>),
+            Gnu(ar::GnuBuilder<File>),
+            NativeAr(&'a Path),
+        }
+
+        let mut builder = if self.config.use_native_ar {
+            BuilderKind::NativeAr(&self.config.dst)
+        } else if self.config.use_gnu_style_archive {
+            BuilderKind::Gnu(ar::GnuBuilder::new(
+                File::create(&self.config.dst).unwrap(),
+                self.entries
+                    .iter()
+                    .map(|(name, _)| name.as_bytes().to_vec())
+                    .collect(),
+            ))
+        } else {
+            BuilderKind::Bsd(ar::Builder::new(File::create(&self.config.dst).unwrap()))
+        };
+
+        // Add all files
+        for (entry_name, entry) in self.entries.into_iter() {
+            match entry {
+                ArchiveEntry::FromArchive {
+                    archive_index,
+                    entry_index,
+                } => {
+                    let (ref src_archive_path, ref mut src_archive) =
+                        self.src_archives[archive_index];
+                    let entry = src_archive.jump_to_entry(entry_index).unwrap();
+                    let header = entry.header().clone();
+
+                    match builder {
+                        BuilderKind::Bsd(ref mut builder) => {
+                            builder.append(&header, entry).unwrap()
+                        }
+                        BuilderKind::Gnu(ref mut builder) => {
+                            builder.append(&header, entry).unwrap()
+                        }
+                        BuilderKind::NativeAr(archive_file) => {
+                            Command::new("ar")
+                                .arg("x")
+                                .arg(src_archive_path)
+                                .arg(&entry_name)
+                                .status()
+                                .unwrap();
+                            add_file_using_ar(archive_file, Path::new(&entry_name));
+                            std::fs::remove_file(entry_name).unwrap();
+                        }
+                    }
+                }
+                ArchiveEntry::File(file) =>
+                    match builder {
+                        BuilderKind::Bsd(ref mut builder) => {
+                            builder
+                                .append_file(entry_name.as_bytes(), &mut File::open(file).expect("file for bsd builder"))
+                                .unwrap()
+                        },
+                        BuilderKind::Gnu(ref mut builder) => {
+                            builder
+                                .append_file(entry_name.as_bytes(), &mut File::open(&file).expect(&format!("file {:?} for gnu builder", file)))
+                                .unwrap()
+                        },
+                        BuilderKind::NativeAr(archive_file) => add_file_using_ar(archive_file, &file),
+                    },
+            }
+        }
+
+        // Finalize archive
+        std::mem::drop(builder);
+
+        // Run ranlib to be able to link the archive
+        let status = std::process::Command::new("ranlib")
+            .arg(self.config.dst)
+            .status()
+            .expect("Couldn't run ranlib");
+
+        if !status.success() {
+            self.config.sess.fatal(&format!("Ranlib exited with code {:?}", status.code()));
+        }
+    }
+
+    fn inject_dll_import_lib(&mut self, _lib_name: &str, _dll_imports: &[DllImport], _tmpdir: &MaybeTempDir) {
+        unimplemented!();
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs
new file mode 100644 (file)
index 0000000..3b77097
--- /dev/null
@@ -0,0 +1,785 @@
+use gccjit::{LValue, RValue, ToRValue, Type};
+use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
+use rustc_codegen_ssa::mir::operand::OperandValue;
+use rustc_codegen_ssa::mir::place::PlaceRef;
+use rustc_codegen_ssa::traits::{AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef};
+
+use rustc_hir::LlvmInlineAsmInner;
+use rustc_middle::{bug, ty::Instance};
+use rustc_span::{Span, Symbol};
+use rustc_target::asm::*;
+
+use std::borrow::Cow;
+
+use crate::builder::Builder;
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+
+// Rust asm! and GCC Extended Asm semantics differ substantially.
+//
+// 1. Rust asm operands go along as one list of operands. Operands themselves indicate 
+//    if they're "in" or "out". "In" and "out" operands can interleave. One operand can be 
+//    both "in" and "out" (`inout(reg)`).
+//
+//    GCC asm has two different lists for "in" and "out" operands. In terms of gccjit, 
+//    this means that all "out" operands must go before "in" operands. "In" and "out" operands 
+//    cannot interleave.
+//
+// 2. Operand lists in both Rust and GCC are indexed. Index starts from 0. Indexes are important 
+//    because the asm template refers to operands by index.
+//
+//    Mapping from Rust to GCC index would be 1-1 if it wasn't for...
+//
+// 3. Clobbers. GCC has a separate list of clobbers, and clobbers don't have indexes. 
+//    Contrary, Rust expresses clobbers through "out" operands that aren't tied to 
+//    a variable (`_`),  and such "clobbers" do have index.
+//
+// 4. Furthermore, GCC Extended Asm does not support explicit register constraints 
+//    (like `out("eax")`) directly, offering so-called "local register variables" 
+//    as a workaround. These variables need to be declared and initialized *before* 
+//    the Extended Asm block but *after* normal local variables 
+//    (see comment in `codegen_inline_asm` for explanation).
+//
+// With that in mind, let's see how we translate Rust syntax to GCC 
+// (from now on, `CC` stands for "constraint code"):
+//
+// * `out(reg_class) var`   -> translated to output operand: `"=CC"(var)`
+// * `inout(reg_class) var` -> translated to output operand: `"+CC"(var)`
+// * `in(reg_class) var`    -> translated to input operand: `"CC"(var)`
+//
+// * `out(reg_class) _` -> translated to one `=r(tmp)`, where "tmp" is a temporary unused variable
+//
+// * `out("explicit register") _` -> not translated to any operands, register is simply added to clobbers list
+//
+// * `inout(reg_class) in_var => out_var` -> translated to two operands: 
+//                              output: `"=CC"(in_var)`
+//                              input:  `"num"(out_var)` where num is the GCC index 
+//                                       of the corresponding output operand
+//
+// * `inout(reg_class) in_var => _` -> same as `inout(reg_class) in_var => tmp`, 
+//                                      where "tmp" is a temporary unused variable
+//
+// * `out/in/inout("explicit register") var` -> translated to one or two operands as described above 
+//                                              with `"r"(var)` constraint, 
+//                                              and one register variable assigned to the desired register.
+// 
+
+const ATT_SYNTAX_INS: &str = ".att_syntax noprefix\n\t";
+const INTEL_SYNTAX_INS: &str = "\n\t.intel_syntax noprefix";
+
+
+struct AsmOutOperand<'a, 'tcx, 'gcc> {
+    rust_idx: usize,
+    constraint: &'a str,
+    late: bool,
+    readwrite: bool,
+
+    tmp_var: LValue<'gcc>,
+    out_place: Option<PlaceRef<'tcx, RValue<'gcc>>>
+}
+
+struct AsmInOperand<'a, 'tcx> {
+    rust_idx: usize,
+    constraint: Cow<'a, str>,
+    val: RValue<'tcx>
+}
+
+impl AsmOutOperand<'_, '_, '_> {
+    fn to_constraint(&self) -> String {
+        let mut res = String::with_capacity(self.constraint.len() + self.late as usize + 1);
+
+        let sign = if self.readwrite { '+' } else { '=' };
+        res.push(sign);
+        if !self.late {
+            res.push('&');
+        }
+
+        res.push_str(&self.constraint);
+        res
+    }
+}
+
+enum ConstraintOrRegister {
+    Constraint(&'static str),
+    Register(&'static str)
+}
+
+
+impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
+    fn codegen_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec<PlaceRef<'tcx, RValue<'gcc>>>, _inputs: Vec<RValue<'gcc>>, span: Span) -> bool {
+        self.sess().struct_span_err(span, "GCC backend does not support `llvm_asm!`")
+            .help("consider using the `asm!` macro instead")
+            .emit();
+
+        // We return `true` even if we've failed to generate the asm
+        // because we want to suppress the "malformed inline assembly" error
+        // generated by the frontend.
+        true
+    }
+
+    fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) {
+        let asm_arch = self.tcx.sess.asm_arch.unwrap();
+        let is_x86 = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64);
+        let att_dialect = is_x86 && options.contains(InlineAsmOptions::ATT_SYNTAX);
+        let intel_dialect = is_x86 && !options.contains(InlineAsmOptions::ATT_SYNTAX);
+
+        // GCC index of an output operand equals its position in the array 
+        let mut outputs = vec![];
+
+        // GCC index of an input operand equals its position in the array
+        // added to `outputs.len()`
+        let mut inputs = vec![];
+
+        // Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _`
+        let mut clobbers = vec![];
+
+        // We're trying to preallocate space for the template
+        let mut constants_len = 0;
+
+        // There are rules we must adhere to if we want GCC to do the right thing:
+        // 
+        // * Every local variable that the asm block uses as an output must be declared *before*
+        //   the asm block. 
+        // * There must be no instructions whatsoever between the register variables and the asm.
+        //
+        // Therefore, the backend must generate the instructions strictly in this order:
+        //
+        // 1. Output variables.
+        // 2. Register variables.
+        // 3. The asm block.
+        //
+        // We also must make sure that no input operands are emitted before output operands.
+        //
+        // This is why we work in passes, first emitting local vars, then local register vars.
+        // Also, we don't emit any asm operands immediately; we save them to 
+        // the one of the buffers to be emitted later.
+
+        // 1. Normal variables (and saving operands to buffers).
+        for (rust_idx, op) in rust_operands.iter().enumerate() {
+            match *op {
+                InlineAsmOperandRef::Out { reg, late, place } => {
+                    use ConstraintOrRegister::*;
+
+                    let (constraint, ty) = match (reg_to_gcc(reg), place) {
+                        (Constraint(constraint), Some(place)) => (constraint, place.layout.gcc_type(self.cx, false)),
+                        // When `reg` is a class and not an explicit register but the out place is not specified,
+                        // we need to create an unused output variable to assign the output to. This var
+                        // needs to be of a type that's "compatible" with the register class, but specific type 
+                        // doesn't matter.
+                        (Constraint(constraint), None) => (constraint, dummy_output_type(self.cx, reg.reg_class())),
+                        (Register(_), Some(_)) => {
+                            // left for the next pass
+                            continue
+                        },
+                        (Register(reg_name), None) => {
+                            // `clobber_abi` can add lots of clobbers that are not supported by the target,
+                            // such as AVX-512 registers, so we just ignore unsupported registers
+                            let is_target_supported = reg.reg_class().supported_types(asm_arch).iter()
+                                .any(|&(_, feature)| {
+                                    if let Some(feature) = feature {
+                                        self.tcx.sess.target_features.contains(&Symbol::intern(feature))
+                                    } else {
+                                        true // Register class is unconditionally supported
+                                    }
+                                });
+
+                            if is_target_supported && !clobbers.contains(&reg_name) {
+                                clobbers.push(reg_name);
+                            }
+                            continue
+                        }
+                    };
+
+                    let tmp_var = self.current_func().new_local(None, ty, "output_register");
+                    outputs.push(AsmOutOperand {
+                        constraint, 
+                        rust_idx,
+                        late,
+                        readwrite: false,
+                        tmp_var,
+                        out_place: place
+                    });
+                }
+
+                InlineAsmOperandRef::In { reg, value } => {
+                    if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
+                        inputs.push(AsmInOperand { 
+                            constraint: Cow::Borrowed(constraint), 
+                            rust_idx, 
+                            val: value.immediate()
+                        });
+                    } 
+                    else {
+                        // left for the next pass
+                        continue
+                    }
+                }
+
+                InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
+                    let constraint = if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) {
+                        constraint
+                    } 
+                    else {
+                        // left for the next pass
+                        continue
+                    };
+
+                    // Rustc frontend guarantees that input and output types are "compatible",
+                    // so we can just use input var's type for the output variable.
+                    //
+                    // This decision is also backed by the fact that LLVM needs in and out 
+                    // values to be of *exactly the same type*, not just "compatible". 
+                    // I'm not sure if GCC is so picky too, but better safe than sorry.
+                    let ty = in_value.layout.gcc_type(self.cx, false);
+                    let tmp_var = self.current_func().new_local(None, ty, "output_register");
+
+                    // If the out_place is None (i.e `inout(reg) _` syntax was used), we translate
+                    // it to one "readwrite (+) output variable", otherwise we translate it to two 
+                    // "out and tied in" vars as described above.
+                    let readwrite = out_place.is_none();
+                    outputs.push(AsmOutOperand {
+                        constraint, 
+                        rust_idx,
+                        late,
+                        readwrite,
+                        tmp_var, 
+                        out_place,
+                    });
+
+                    if !readwrite {
+                        let out_gcc_idx = outputs.len() - 1;
+                        let constraint = Cow::Owned(out_gcc_idx.to_string());
+
+                        inputs.push(AsmInOperand {
+                            constraint, 
+                            rust_idx, 
+                            val: in_value.immediate()
+                        });
+                    }
+                }
+
+                InlineAsmOperandRef::Const { ref string } => {
+                    constants_len += string.len() + att_dialect as usize;
+                }
+
+                InlineAsmOperandRef::SymFn { instance } => {
+                    constants_len += self.tcx.symbol_name(instance).name.len();
+                }
+                InlineAsmOperandRef::SymStatic { def_id } => {
+                    constants_len += self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len();
+                }
+            }
+        }
+
+        // 2. Register variables.
+        for (rust_idx, op) in rust_operands.iter().enumerate() {
+            match *op {
+                // `out("explicit register") var`
+                InlineAsmOperandRef::Out { reg, late, place } => {
+                    if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
+                        let out_place = if let Some(place) = place {
+                            place
+                        } 
+                        else {
+                            // processed in the previous pass
+                            continue
+                        };
+
+                        let ty = out_place.layout.gcc_type(self.cx, false);
+                        let tmp_var = self.current_func().new_local(None, ty, "output_register");
+                        tmp_var.set_register_name(reg_name);
+
+                        outputs.push(AsmOutOperand {
+                            constraint: "r".into(), 
+                            rust_idx,
+                            late,
+                            readwrite: false,
+                            tmp_var,
+                            out_place: Some(out_place)
+                        });
+                    }
+
+                    // processed in the previous pass
+                }
+
+                // `in("explicit register") var`
+                InlineAsmOperandRef::In { reg, value } => {
+                    if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
+                        let ty = value.layout.gcc_type(self.cx, false);
+                        let reg_var = self.current_func().new_local(None, ty, "input_register");
+                        reg_var.set_register_name(reg_name);
+                        self.llbb().add_assignment(None, reg_var, value.immediate());
+
+                        inputs.push(AsmInOperand { 
+                            constraint: "r".into(), 
+                            rust_idx, 
+                            val: reg_var.to_rvalue()
+                        });
+                    }
+
+                    // processed in the previous pass
+                }
+
+                // `inout("explicit register") in_var => out_var`
+                InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => {
+                    if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) {
+                        let out_place = if let Some(place) = out_place {
+                            place
+                        } 
+                        else {
+                            // processed in the previous pass
+                            continue
+                        };
+
+                        // See explanation in the first pass.
+                        let ty = in_value.layout.gcc_type(self.cx, false);
+                        let tmp_var = self.current_func().new_local(None, ty, "output_register");
+                        tmp_var.set_register_name(reg_name);
+
+                        outputs.push(AsmOutOperand {
+                            constraint: "r".into(), 
+                            rust_idx,
+                            late,
+                            readwrite: false,
+                            tmp_var,
+                            out_place: Some(out_place)
+                        });
+
+                        let constraint = Cow::Owned((outputs.len() - 1).to_string());
+                        inputs.push(AsmInOperand { 
+                            constraint, 
+                            rust_idx,
+                            val: in_value.immediate()
+                        });
+                    }
+
+                    // processed in the previous pass
+                }
+
+                InlineAsmOperandRef::Const { .. } 
+                | InlineAsmOperandRef::SymFn { .. } 
+                | InlineAsmOperandRef::SymStatic { .. } => {
+                    // processed in the previous pass
+                }
+            }
+        }
+
+        // 3. Build the template string
+
+        let mut template_str = String::with_capacity(estimate_template_length(template, constants_len, att_dialect));
+        if !intel_dialect {
+            template_str.push_str(ATT_SYNTAX_INS);
+        }
+
+        for piece in template {
+            match *piece {
+                InlineAsmTemplatePiece::String(ref string) => {
+                    // TODO(@Commeownist): switch to `Iterator::intersperse` once it's stable
+                    let mut iter = string.split('%');
+                    if let Some(s) = iter.next() {
+                        template_str.push_str(s);
+                    }
+
+                    for s in iter {
+                        template_str.push_str("%%");
+                        template_str.push_str(s);
+                    }
+                }
+                InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => {
+                    let mut push_to_template = |modifier, gcc_idx| {
+                        use std::fmt::Write;
+
+                        template_str.push('%');
+                        if let Some(modifier) = modifier {
+                            template_str.push(modifier);
+                        }
+                        write!(template_str, "{}", gcc_idx).expect("pushing to string failed");
+                    };
+
+                    match rust_operands[operand_idx] {
+                        InlineAsmOperandRef::Out { reg, ..  } => {
+                            let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
+                            let gcc_index = outputs.iter()
+                                .position(|op| operand_idx == op.rust_idx)
+                                .expect("wrong rust index");
+                            push_to_template(modifier, gcc_index);
+                        }
+
+                        InlineAsmOperandRef::In { reg, .. } => {
+                            let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
+                            let in_gcc_index = inputs.iter()
+                                .position(|op| operand_idx == op.rust_idx)
+                                .expect("wrong rust index");
+                            let gcc_index = in_gcc_index + outputs.len();
+                            push_to_template(modifier, gcc_index);
+                        }
+
+                        InlineAsmOperandRef::InOut { reg, .. } => {
+                            let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier);
+
+                            // The input register is tied to the output, so we can just use the index of the output register
+                            let gcc_index = outputs.iter()
+                                .position(|op| operand_idx == op.rust_idx)
+                                .expect("wrong rust index");
+                            push_to_template(modifier, gcc_index);
+                        }
+
+                        InlineAsmOperandRef::SymFn { instance } => {
+                            let name = self.tcx.symbol_name(instance).name;
+                            template_str.push_str(name);
+                        }
+
+                        InlineAsmOperandRef::SymStatic { def_id } => {
+                            // TODO(@Commeownist): This may not be sufficient for all kinds of statics.
+                            // Some statics may need the `@plt` suffix, like thread-local vars.
+                            let instance = Instance::mono(self.tcx, def_id);
+                            let name = self.tcx.symbol_name(instance).name;
+                            template_str.push_str(name);
+                        }
+
+                        InlineAsmOperandRef::Const { ref string } => {
+                            // Const operands get injected directly into the template
+                            if att_dialect {
+                                template_str.push('$');
+                            }
+                            template_str.push_str(string);
+                        }
+                    }
+                }
+            }
+        }
+
+        if !intel_dialect {
+            template_str.push_str(INTEL_SYNTAX_INS);
+        }
+        
+        // 4. Generate Extended Asm block
+
+        let block = self.llbb();
+        let extended_asm = block.add_extended_asm(None, &template_str);
+
+        for op in &outputs {
+            extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var);
+        }
+
+        for op in &inputs {
+            extended_asm.add_input_operand(None, &op.constraint, op.val);
+        }
+
+        for clobber in clobbers.iter() {
+            extended_asm.add_clobber(clobber);
+        }
+
+        if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) {
+            // TODO(@Commeownist): I'm not 100% sure this one clobber is sufficient 
+            // on all architectures. For instance, what about FP stack?
+            extended_asm.add_clobber("cc");
+        }
+        if !options.contains(InlineAsmOptions::NOMEM) {
+            extended_asm.add_clobber("memory");
+        }
+        if !options.contains(InlineAsmOptions::PURE) {
+            extended_asm.set_volatile_flag(true);
+        }
+        if !options.contains(InlineAsmOptions::NOSTACK) {
+            // TODO(@Commeownist): figure out how to align stack
+        }
+        if options.contains(InlineAsmOptions::NORETURN) {
+            let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
+            let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) };
+            self.call(self.type_void(), builtin_unreachable, &[], None);
+        }
+
+        // Write results to outputs. 
+        //
+        // We need to do this because:
+        //  1. Turning `PlaceRef` into `RValue` is error-prone and has nasty edge cases 
+        //     (especially with current `rustc_backend_ssa` API).
+        //  2. Not every output operand has an `out_place`, and it's required by `add_output_operand`.
+        //
+        // Instead, we generate a temporary output variable for each output operand, and then this loop,
+        // generates `out_place = tmp_var;` assignments if out_place exists.
+        for op in &outputs {
+            if let Some(place) = op.out_place {
+                OperandValue::Immediate(op.tmp_var.to_rvalue()).store(self, place);                
+            }
+        }
+
+    }
+}
+
+fn estimate_template_length(template: &[InlineAsmTemplatePiece], constants_len: usize, att_dialect: bool) -> usize {
+    let len: usize = template.iter().map(|piece| {
+        match *piece {
+            InlineAsmTemplatePiece::String(ref string) => {
+                string.len()
+            }
+            InlineAsmTemplatePiece::Placeholder { .. } => {
+                // '%' + 1 char modifier + 1 char index
+                3
+            }
+        }
+    })
+    .sum();
+
+    // increase it by 5% to account for possible '%' signs that'll be duplicated
+    // I pulled the number out of blue, but should be fair enough
+    // as the upper bound
+    let mut res = (len as f32 * 1.05) as usize + constants_len;
+
+    if att_dialect {
+        res += INTEL_SYNTAX_INS.len() + ATT_SYNTAX_INS.len();
+    }
+    res
+}
+
+/// Converts a register class to a GCC constraint code.
+fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
+    let constraint = match reg {
+        // For vector registers LLVM wants the register name to match the type size.
+        InlineAsmRegOrRegClass::Reg(reg) => {
+            match reg {
+                InlineAsmReg::X86(_) => {
+                    // TODO(antoyo): add support for vector register.
+                    //
+                    // // For explicit registers, we have to create a register variable: https://stackoverflow.com/a/31774784/389119
+                    return ConstraintOrRegister::Register(match reg.name() {
+                        // Some of registers' names does not map 1-1 from rust to gcc
+                        "st(0)" => "st",
+
+                        name => name,
+                    });
+                }
+
+                _ => unimplemented!(),
+            }
+        },
+        InlineAsmRegOrRegClass::RegClass(reg) => match reg {
+            InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(),
+            InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => unimplemented!(),
+            InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => unimplemented!(),
+            InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => unimplemented!(),
+            InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => unimplemented!(),
+            InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(),
+            InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
+            | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
+            | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) => unimplemented!(),
+            InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16)
+            | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8)
+            | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => unimplemented!(),
+            InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
+            | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => unimplemented!(),
+            InlineAsmRegClass::Bpf(_) => unimplemented!(),
+            InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => unimplemented!(),
+            InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => unimplemented!(),
+            InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => unimplemented!(),
+            InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => unimplemented!(),
+            InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => unimplemented!(),
+            InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => unimplemented!(),
+            InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => unimplemented!(),
+            InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => unimplemented!(),
+            InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => unimplemented!(),
+            InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
+            | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
+                unreachable!("clobber-only")
+            },
+            InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => unimplemented!(),
+            InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(),
+            InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(),
+            InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r",
+            InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q",
+            InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q",
+            InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
+            | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x",
+            InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v",
+            InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(),
+            InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
+            InlineAsmRegClass::X86(
+                X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg,
+            ) => unreachable!("clobber-only"),
+            InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
+                bug!("GCC backend does not support SPIR-V")
+            }
+            InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(),
+            InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(),
+            InlineAsmRegClass::Err => unreachable!(),
+        }
+    };
+
+    ConstraintOrRegister::Constraint(constraint)
+}
+
+/// Type to use for outputs that are discarded. It doesn't really matter what
+/// the type is, as long as it is valid for the constraint code.
+fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegClass) -> Type<'gcc> {
+    match reg {
+        InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => cx.type_i32(),
+        InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(),
+        InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg)
+        | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
+            unimplemented!()
+        }
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(),
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(),
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => cx.type_f64(),
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
+            unimplemented!()
+        }
+        InlineAsmRegClass::Bpf(_) => unimplemented!(),
+        InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
+        InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(),
+        InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(),
+        InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(),
+        InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(),
+        InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(),
+        InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(),
+        InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(),
+        InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(),
+        InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr)
+        | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => {
+            unreachable!("clobber-only")
+        },
+        InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(),
+        InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(),
+        InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => cx.type_f32(),
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
+        | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(),
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(),
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(),
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg)
+        | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg)
+        | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(),
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(),
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(),
+        InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
+        InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
+            bug!("LLVM backend does not support SPIR-V")
+        },
+        InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
+        InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
+        InlineAsmRegClass::Err => unreachable!(),
+    }
+}
+
+impl<'gcc, 'tcx> AsmMethods for CodegenCx<'gcc, 'tcx> {
+    fn codegen_global_asm(&self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef], options: InlineAsmOptions, _line_spans: &[Span]) {
+        let asm_arch = self.tcx.sess.asm_arch.unwrap();
+
+        // Default to Intel syntax on x86
+        let intel_syntax = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64)
+            && !options.contains(InlineAsmOptions::ATT_SYNTAX);
+
+        // Build the template string
+        let mut template_str = String::new();
+        for piece in template {
+            match *piece {
+                InlineAsmTemplatePiece::String(ref string) => {
+                    for line in string.lines() {
+                        // NOTE: gcc does not allow inline comment, so remove them.
+                        let line =
+                            if let Some(index) = line.rfind("//") {
+                                &line[..index]
+                            }
+                            else {
+                                line
+                            };
+                        template_str.push_str(line);
+                        template_str.push('\n');
+                    }
+                },
+                InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => {
+                    match operands[operand_idx] {
+                        GlobalAsmOperandRef::Const { ref string } => {
+                            // Const operands get injected directly into the
+                            // template. Note that we don't need to escape %
+                            // here unlike normal inline assembly.
+                            template_str.push_str(string);
+                        }
+                    }
+                }
+            }
+        }
+
+        let template_str =
+            if intel_syntax {
+                format!("{}\n\t.intel_syntax noprefix", template_str)
+            }
+            else {
+                format!(".att_syntax\n\t{}\n\t.intel_syntax noprefix", template_str)
+            };
+        // NOTE: seems like gcc will put the asm in the wrong section, so set it to .text manually.
+        let template_str = format!(".pushsection .text\n{}\n.popsection", template_str);
+        self.context.add_top_level_asm(None, &template_str);
+    }
+}
+
+fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option<char>) -> Option<char> {
+    match reg {
+        InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => modifier,
+        InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => modifier,
+        InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg)
+        | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => {
+            unimplemented!()
+        }
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(),
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => unimplemented!(),
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => unimplemented!(),
+        InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8)
+        | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
+            unimplemented!()
+        }
+        InlineAsmRegClass::Bpf(_) => unimplemented!(),
+        InlineAsmRegClass::Hexagon(_) => unimplemented!(),
+        InlineAsmRegClass::Mips(_) => unimplemented!(),
+        InlineAsmRegClass::Nvptx(_) => unimplemented!(),
+        InlineAsmRegClass::PowerPC(_) => unimplemented!(),
+        InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg)
+        | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(),
+        InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(),
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::reg)
+        | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier {
+            None => if arch == InlineAsmArch::X86_64 { Some('q') } else { Some('k') },
+            Some('l') => Some('b'),
+            Some('h') => Some('h'),
+            Some('x') => Some('w'),
+            Some('e') => Some('k'),
+            Some('r') => Some('q'),
+            _ => unreachable!(),
+        },
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => None,
+        InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::xmm_reg)
+        | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::ymm_reg)
+        | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::zmm_reg) => match (reg, modifier) {
+            (X86InlineAsmRegClass::xmm_reg, None) => Some('x'),
+            (X86InlineAsmRegClass::ymm_reg, None) => Some('t'),
+            (X86InlineAsmRegClass::zmm_reg, None) => Some('g'),
+            (_, Some('x')) => Some('x'),
+            (_, Some('y')) => Some('t'),
+            (_, Some('z')) => Some('g'),
+            _ => unreachable!(),
+        },
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None,
+        InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => {
+            unreachable!("clobber-only")
+        }
+        InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(),
+        InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
+            bug!("LLVM backend does not support SPIR-V")
+        },
+        InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => unimplemented!(),
+        InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => unimplemented!(),
+        InlineAsmRegClass::Err => unreachable!(),
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/back/mod.rs b/compiler/rustc_codegen_gcc/src/back/mod.rs
new file mode 100644 (file)
index 0000000..d692799
--- /dev/null
@@ -0,0 +1 @@
+pub mod write;
diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs
new file mode 100644 (file)
index 0000000..c3e3847
--- /dev/null
@@ -0,0 +1,78 @@
+use std::fs;
+
+use gccjit::OutputKind;
+use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
+use rustc_codegen_ssa::back::write::{CodegenContext, EmitObj, ModuleConfig};
+use rustc_errors::Handler;
+use rustc_session::config::OutputType;
+use rustc_span::fatal_error::FatalError;
+use rustc_target::spec::SplitDebuginfo;
+
+use crate::{GccCodegenBackend, GccContext};
+
+pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_handler: &Handler, module: ModuleCodegen<GccContext>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
+    let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &module.name[..]);
+    {
+        let context = &module.module_llvm.context;
+
+        let module_name = module.name.clone();
+        let module_name = Some(&module_name[..]);
+
+        let _bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
+        let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
+
+        if config.bitcode_needed() {
+            // TODO(antoyo)
+        }
+
+        if config.emit_ir {
+            unimplemented!();
+        }
+
+        if config.emit_asm {
+            let _timer = cgcx
+                .prof
+                .generic_activity_with_arg("LLVM_module_codegen_emit_asm", &module.name[..]);
+            let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
+            context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str"));
+        }
+
+        match config.emit_obj {
+            EmitObj::ObjectCode(_) => {
+                let _timer = cgcx
+                    .prof
+                    .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]);
+                match &*module.name {
+                    "std_example.7rcbfp3g-cgu.15" => {
+                        println!("Dumping reproducer {}", module.name);
+                        let _ = fs::create_dir("/tmp/reproducers");
+                        // FIXME(antoyo): segfault in dump_reproducer_to_file() might be caused by
+                        // transmuting an rvalue to an lvalue.
+                        // Segfault is actually in gcc::jit::reproducer::get_identifier_as_lvalue
+                        context.dump_reproducer_to_file(&format!("/tmp/reproducers/{}.c", module.name));
+                        println!("Dumped reproducer {}", module.name);
+                    },
+                    _ => (),
+                }
+                context.compile_to_file(OutputKind::ObjectFile, obj_out.to_str().expect("path to str"));
+            }
+
+            EmitObj::Bitcode => {
+                // TODO(antoyo)
+            }
+
+            EmitObj::None => {}
+        }
+    }
+
+    Ok(module.into_compiled_module(
+        config.emit_obj != EmitObj::None,
+        cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked,
+        config.emit_bc,
+        &cgcx.output_filenames,
+    ))
+}
+
+pub(crate) fn link(_cgcx: &CodegenContext<GccCodegenBackend>, _diag_handler: &Handler, mut _modules: Vec<ModuleCodegen<GccContext>>) -> Result<ModuleCodegen<GccContext>, FatalError> {
+    unimplemented!();
+}
diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs
new file mode 100644 (file)
index 0000000..9f96096
--- /dev/null
@@ -0,0 +1,165 @@
+use std::env;
+use std::time::Instant;
+
+use gccjit::{
+    Context,
+    FunctionType,
+    GlobalKind,
+};
+use rustc_middle::dep_graph;
+use rustc_middle::middle::exported_symbols;
+use rustc_middle::ty::TyCtxt;
+use rustc_middle::mir::mono::Linkage;
+use rustc_codegen_ssa::{ModuleCodegen, ModuleKind};
+use rustc_codegen_ssa::base::maybe_create_entry_wrapper;
+use rustc_codegen_ssa::mono_item::MonoItemExt;
+use rustc_codegen_ssa::traits::DebugInfoMethods;
+use rustc_metadata::EncodedMetadata;
+use rustc_session::config::DebugInfo;
+use rustc_span::Symbol;
+
+use crate::GccContext;
+use crate::builder::Builder;
+use crate::context::CodegenCx;
+
+pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind {
+    match linkage {
+        Linkage::External => GlobalKind::Imported,
+        Linkage::AvailableExternally => GlobalKind::Imported,
+        Linkage::LinkOnceAny => unimplemented!(),
+        Linkage::LinkOnceODR => unimplemented!(),
+        Linkage::WeakAny => unimplemented!(),
+        Linkage::WeakODR => unimplemented!(),
+        Linkage::Appending => unimplemented!(),
+        Linkage::Internal => GlobalKind::Internal,
+        Linkage::Private => GlobalKind::Internal,
+        Linkage::ExternalWeak => GlobalKind::Imported, // TODO(antoyo): should be weak linkage.
+        Linkage::Common => unimplemented!(),
+    }
+}
+
+pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType {
+    match linkage {
+        Linkage::External => FunctionType::Exported,
+        Linkage::AvailableExternally => FunctionType::Extern,
+        Linkage::LinkOnceAny => unimplemented!(),
+        Linkage::LinkOnceODR => unimplemented!(),
+        Linkage::WeakAny => FunctionType::Exported, // FIXME(antoyo): should be similar to linkonce.
+        Linkage::WeakODR => unimplemented!(),
+        Linkage::Appending => unimplemented!(),
+        Linkage::Internal => FunctionType::Internal,
+        Linkage::Private => FunctionType::Internal,
+        Linkage::ExternalWeak => unimplemented!(),
+        Linkage::Common => unimplemented!(),
+    }
+}
+
+pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<GccContext>, u64) {
+    let prof_timer = tcx.prof.generic_activity("codegen_module");
+    let start_time = Instant::now();
+
+    let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx);
+    let (module, _) = tcx.dep_graph.with_task(dep_node, tcx, cgu_name, module_codegen, dep_graph::hash_result);
+    let time_to_codegen = start_time.elapsed();
+    drop(prof_timer);
+
+    // We assume that the cost to run GCC on a CGU is proportional to
+    // the time we needed for codegenning it.
+    let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64;
+
+    fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen<GccContext> {
+        let cgu = tcx.codegen_unit(cgu_name);
+        // Instantiate monomorphizations without filling out definitions yet...
+        //let llvm_module = ModuleLlvm::new(tcx, &cgu_name.as_str());
+        let context = Context::default();
+        // TODO(antoyo): only set on x86 platforms.
+        context.add_command_line_option("-masm=intel");
+        for arg in &tcx.sess.opts.cg.llvm_args {
+            context.add_command_line_option(arg);
+        }
+        context.add_command_line_option("-fno-semantic-interposition");
+        if env::var("CG_GCCJIT_DUMP_CODE").as_deref() == Ok("1") {
+            context.set_dump_code_on_compile(true);
+        }
+        if env::var("CG_GCCJIT_DUMP_GIMPLE").as_deref() == Ok("1") {
+            context.set_dump_initial_gimple(true);
+        }
+        context.set_debug_info(true);
+        if env::var("CG_GCCJIT_DUMP_EVERYTHING").as_deref() == Ok("1") {
+            context.set_dump_everything(true);
+        }
+        if env::var("CG_GCCJIT_KEEP_INTERMEDIATES").as_deref() == Ok("1") {
+            context.set_keep_intermediates(true);
+        }
+
+        {
+            let cx = CodegenCx::new(&context, cgu, tcx);
+
+            let mono_items = cgu.items_in_deterministic_order(tcx);
+            for &(mono_item, (linkage, visibility)) in &mono_items {
+                mono_item.predefine::<Builder<'_, '_, '_>>(&cx, linkage, visibility);
+            }
+
+            // ... and now that we have everything pre-defined, fill out those definitions.
+            for &(mono_item, _) in &mono_items {
+                mono_item.define::<Builder<'_, '_, '_>>(&cx);
+            }
+
+            // If this codegen unit contains the main function, also create the
+            // wrapper here
+            maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx);
+
+            // Finalize debuginfo
+            if cx.sess().opts.debuginfo != DebugInfo::None {
+                cx.debuginfo_finalize();
+            }
+        }
+
+        ModuleCodegen {
+            name: cgu_name.to_string(),
+            module_llvm: GccContext {
+                context
+            },
+            kind: ModuleKind::Regular,
+        }
+    }
+
+    (module, cost)
+}
+
+pub fn write_compressed_metadata<'tcx>(tcx: TyCtxt<'tcx>, metadata: &EncodedMetadata, gcc_module: &mut GccContext) {
+    use snap::write::FrameEncoder;
+    use std::io::Write;
+
+    // Historical note:
+    //
+    // When using link.exe it was seen that the section name `.note.rustc`
+    // was getting shortened to `.note.ru`, and according to the PE and COFF
+    // specification:
+    //
+    // > Executable images do not use a string table and do not support
+    // > section names longer than 8 characters
+    //
+    // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
+    //
+    // As a result, we choose a slightly shorter name! As to why
+    // `.note.rustc` works on MinGW, see
+    // https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197
+    let section_name = if tcx.sess.target.is_like_osx { "__DATA,.rustc" } else { ".rustc" };
+
+    let context = &gcc_module.context;
+    let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
+    FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data()).unwrap();
+
+    let name = exported_symbols::metadata_symbol_name(tcx);
+    let typ = context.new_array_type(None, context.new_type::<u8>(), compressed.len() as i32);
+    let global = context.new_global(None, GlobalKind::Exported, typ, name);
+    global.global_set_initializer(&compressed);
+    global.set_link_section(section_name);
+
+    // Also generate a .section directive to force no
+    // flags, at least for ELF outputs, so that the
+    // metadata doesn't get loaded into memory.
+    let directive = format!(".section {}", section_name);
+    context.add_top_level_asm(None, &directive);
+}
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
new file mode 100644 (file)
index 0000000..ac90841
--- /dev/null
@@ -0,0 +1,1540 @@
+use std::borrow::Cow;
+use std::cell::Cell;
+use std::convert::TryFrom;
+use std::ops::Deref;
+
+use gccjit::FunctionType;
+use gccjit::{
+    BinaryOp,
+    Block,
+    ComparisonOp,
+    Function,
+    LValue,
+    RValue,
+    ToRValue,
+    Type,
+    UnaryOp,
+};
+use rustc_codegen_ssa::MemFlags;
+use rustc_codegen_ssa::common::{AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope};
+use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
+use rustc_codegen_ssa::mir::place::PlaceRef;
+use rustc_codegen_ssa::traits::{
+    BackendTypes,
+    BaseTypeMethods,
+    BuilderMethods,
+    ConstMethods,
+    DerivedTypeMethods,
+    LayoutTypeMethods,
+    HasCodegen,
+    OverflowOp,
+    StaticBuilderMethods,
+};
+use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
+use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
+use rustc_span::Span;
+use rustc_span::def_id::DefId;
+use rustc_target::abi::{
+    self,
+    call::FnAbi,
+    Align,
+    HasDataLayout,
+    Size,
+    TargetDataLayout,
+    WrappingRange,
+};
+use rustc_target::spec::{HasTargetSpec, Target};
+
+use crate::common::{SignType, TypeReflection, type_is_pointer};
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+// TODO(antoyo)
+type Funclet = ();
+
+// TODO(antoyo): remove this variable.
+static mut RETURN_VALUE_COUNT: usize = 0;
+
+enum ExtremumOperation {
+    Max,
+    Min,
+}
+
+trait EnumClone {
+    fn clone(&self) -> Self;
+}
+
+impl EnumClone for AtomicOrdering {
+    fn clone(&self) -> Self {
+        match *self {
+            AtomicOrdering::NotAtomic => AtomicOrdering::NotAtomic,
+            AtomicOrdering::Unordered => AtomicOrdering::Unordered,
+            AtomicOrdering::Monotonic => AtomicOrdering::Monotonic,
+            AtomicOrdering::Acquire => AtomicOrdering::Acquire,
+            AtomicOrdering::Release => AtomicOrdering::Release,
+            AtomicOrdering::AcquireRelease => AtomicOrdering::AcquireRelease,
+            AtomicOrdering::SequentiallyConsistent => AtomicOrdering::SequentiallyConsistent,
+        }
+    }
+}
+
+pub struct Builder<'a: 'gcc, 'gcc, 'tcx> {
+    pub cx: &'a CodegenCx<'gcc, 'tcx>,
+    pub block: Option<Block<'gcc>>,
+    stack_var_count: Cell<usize>,
+}
+
+impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
+    fn with_cx(cx: &'a CodegenCx<'gcc, 'tcx>) -> Self {
+        Builder {
+            cx,
+            block: None,
+            stack_var_count: Cell::new(0),
+        }
+    }
+
+    fn atomic_extremum(&mut self, operation: ExtremumOperation, dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering) -> RValue<'gcc> {
+        let size = self.cx.int_width(src.get_type()) / 8;
+
+        let func = self.current_func();
+
+        let load_ordering =
+            match order {
+                // TODO(antoyo): does this make sense?
+                AtomicOrdering::AcquireRelease | AtomicOrdering::Release => AtomicOrdering::Acquire,
+                _ => order.clone(),
+            };
+        let previous_value = self.atomic_load(dst.get_type(), dst, load_ordering.clone(), Size::from_bytes(size));
+        let previous_var = func.new_local(None, previous_value.get_type(), "previous_value");
+        let return_value = func.new_local(None, previous_value.get_type(), "return_value");
+        self.llbb().add_assignment(None, previous_var, previous_value);
+        self.llbb().add_assignment(None, return_value, previous_var.to_rvalue());
+
+        let while_block = func.new_block("while");
+        let after_block = func.new_block("after_while");
+        self.llbb().end_with_jump(None, while_block);
+
+        // NOTE: since jumps were added and compare_exchange doesn't expect this, the current blocks in the
+        // state need to be updated.
+        self.block = Some(while_block);
+        *self.cx.current_block.borrow_mut() = Some(while_block);
+
+        let comparison_operator =
+            match operation {
+                ExtremumOperation::Max => ComparisonOp::LessThan,
+                ExtremumOperation::Min => ComparisonOp::GreaterThan,
+            };
+
+        let cond1 = self.context.new_comparison(None, comparison_operator, previous_var.to_rvalue(), self.context.new_cast(None, src, previous_value.get_type()));
+        let compare_exchange = self.compare_exchange(dst, previous_var, src, order, load_ordering, false);
+        let cond2 = self.cx.context.new_unary_op(None, UnaryOp::LogicalNegate, compare_exchange.get_type(), compare_exchange);
+        let cond = self.cx.context.new_binary_op(None, BinaryOp::LogicalAnd, self.cx.bool_type, cond1, cond2);
+
+        while_block.end_with_conditional(None, cond, while_block, after_block);
+
+        // NOTE: since jumps were added in a place rustc does not expect, the current blocks in the
+        // state need to be updated.
+        self.block = Some(after_block);
+        *self.cx.current_block.borrow_mut() = Some(after_block);
+
+        return_value.to_rvalue()
+    }
+
+    fn compare_exchange(&self, dst: RValue<'gcc>, cmp: LValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> {
+        let size = self.cx.int_width(src.get_type());
+        let compare_exchange = self.context.get_builtin_function(&format!("__atomic_compare_exchange_{}", size / 8));
+        let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
+        let failure_order = self.context.new_rvalue_from_int(self.i32_type, failure_order.to_gcc());
+        let weak = self.context.new_rvalue_from_int(self.bool_type, weak as i32);
+
+        let void_ptr_type = self.context.new_type::<*mut ()>();
+        let volatile_void_ptr_type = void_ptr_type.make_volatile();
+        let dst = self.context.new_cast(None, dst, volatile_void_ptr_type);
+        let expected = self.context.new_cast(None, cmp.get_address(None), void_ptr_type);
+
+        // NOTE: not sure why, but we have the wrong type here.
+        let int_type = compare_exchange.get_param(2).to_rvalue().get_type();
+        let src = self.context.new_cast(None, src, int_type);
+        self.context.new_call(None, compare_exchange, &[dst, expected, src, weak, order, failure_order])
+    }
+
+    pub fn assign(&self, lvalue: LValue<'gcc>, value: RValue<'gcc>) {
+        self.llbb().add_assignment(None, lvalue, value);
+    }
+
+    fn check_call<'b>(&mut self, _typ: &str, func: Function<'gcc>, args: &'b [RValue<'gcc>]) -> Cow<'b, [RValue<'gcc>]> {
+        let mut all_args_match = true;
+        let mut param_types = vec![];
+        let param_count = func.get_param_count();
+        for (index, arg) in args.iter().enumerate().take(param_count) {
+            let param = func.get_param(index as i32);
+            let param = param.to_rvalue().get_type();
+            if param != arg.get_type() {
+                all_args_match = false;
+            }
+            param_types.push(param);
+        }
+
+        if all_args_match {
+            return Cow::Borrowed(args);
+        }
+
+        let casted_args: Vec<_> = param_types
+            .into_iter()
+            .zip(args.iter())
+            .enumerate()
+            .map(|(_i, (expected_ty, &actual_val))| {
+                let actual_ty = actual_val.get_type();
+                if expected_ty != actual_ty {
+                    self.bitcast(actual_val, expected_ty)
+                }
+                else {
+                    actual_val
+                }
+            })
+            .collect();
+
+        Cow::Owned(casted_args)
+    }
+
+    fn check_ptr_call<'b>(&mut self, _typ: &str, func_ptr: RValue<'gcc>, args: &'b [RValue<'gcc>]) -> Cow<'b, [RValue<'gcc>]> {
+        let mut all_args_match = true;
+        let mut param_types = vec![];
+        let gcc_func = func_ptr.get_type().is_function_ptr_type().expect("function ptr");
+        for (index, arg) in args.iter().enumerate().take(gcc_func.get_param_count()) {
+            let param = gcc_func.get_param_type(index);
+            if param != arg.get_type() {
+                all_args_match = false;
+            }
+            param_types.push(param);
+        }
+
+        if all_args_match {
+            return Cow::Borrowed(args);
+        }
+
+        let casted_args: Vec<_> = param_types
+            .into_iter()
+            .zip(args.iter())
+            .enumerate()
+            .map(|(_i, (expected_ty, &actual_val))| {
+                let actual_ty = actual_val.get_type();
+                if expected_ty != actual_ty {
+                    self.bitcast(actual_val, expected_ty)
+                }
+                else {
+                    actual_val
+                }
+            })
+            .collect();
+
+        Cow::Owned(casted_args)
+    }
+
+    fn check_store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> {
+        let dest_ptr_ty = self.cx.val_ty(ptr).make_pointer(); // TODO(antoyo): make sure make_pointer() is okay here.
+        let stored_ty = self.cx.val_ty(val);
+        let stored_ptr_ty = self.cx.type_ptr_to(stored_ty);
+
+        if dest_ptr_ty == stored_ptr_ty {
+            ptr
+        }
+        else {
+            self.bitcast(ptr, stored_ptr_ty)
+        }
+    }
+
+    pub fn current_func(&self) -> Function<'gcc> {
+        self.block.expect("block").get_function()
+    }
+
+    fn function_call(&mut self, func: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
+        // TODO(antoyo): remove when the API supports a different type for functions.
+        let func: Function<'gcc> = self.cx.rvalue_as_function(func);
+        let args = self.check_call("call", func, args);
+
+        // gccjit requires to use the result of functions, even when it's not used.
+        // That's why we assign the result to a local or call add_eval().
+        let return_type = func.get_return_type();
+        let current_block = self.current_block.borrow().expect("block");
+        let void_type = self.context.new_type::<()>();
+        let current_func = current_block.get_function();
+        if return_type != void_type {
+            unsafe { RETURN_VALUE_COUNT += 1 };
+            let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
+            current_block.add_assignment(None, result, self.cx.context.new_call(None, func, &args));
+            result.to_rvalue()
+        }
+        else {
+            current_block.add_eval(None, self.cx.context.new_call(None, func, &args));
+            // Return dummy value when not having return value.
+            self.context.new_rvalue_from_long(self.isize_type, 0)
+        }
+    }
+
+    fn function_ptr_call(&mut self, func_ptr: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
+        let args = self.check_ptr_call("call", func_ptr, args);
+
+        // gccjit requires to use the result of functions, even when it's not used.
+        // That's why we assign the result to a local or call add_eval().
+        let gcc_func = func_ptr.get_type().is_function_ptr_type().expect("function ptr");
+        let mut return_type = gcc_func.get_return_type();
+        let current_block = self.current_block.borrow().expect("block");
+        let void_type = self.context.new_type::<()>();
+        let current_func = current_block.get_function();
+
+        // FIXME(antoyo): As a temporary workaround for unsupported LLVM intrinsics.
+        if gcc_func.get_param_count() == 0 && format!("{:?}", func_ptr) == "__builtin_ia32_pmovmskb128" {
+            return_type = self.int_type;
+        }
+
+        if return_type != void_type {
+            unsafe { RETURN_VALUE_COUNT += 1 };
+            let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
+            current_block.add_assignment(None, result, self.cx.context.new_call_through_ptr(None, func_ptr, &args));
+            result.to_rvalue()
+        }
+        else {
+            if gcc_func.get_param_count() == 0 {
+                // FIXME(antoyo): As a temporary workaround for unsupported LLVM intrinsics.
+                current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &[]));
+            }
+            else {
+                current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &args));
+            }
+            // Return dummy value when not having return value.
+            let result = current_func.new_local(None, self.isize_type, "dummyValueThatShouldNeverBeUsed");
+            current_block.add_assignment(None, result, self.context.new_rvalue_from_long(self.isize_type, 0));
+            result.to_rvalue()
+        }
+    }
+
+    pub fn overflow_call(&mut self, func: Function<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
+        // gccjit requires to use the result of functions, even when it's not used.
+        // That's why we assign the result to a local.
+        let return_type = self.context.new_type::<bool>();
+        let current_block = self.current_block.borrow().expect("block");
+        let current_func = current_block.get_function();
+        // TODO(antoyo): return the new_call() directly? Since the overflow function has no side-effects.
+        unsafe { RETURN_VALUE_COUNT += 1 };
+        let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT }));
+        current_block.add_assignment(None, result, self.cx.context.new_call(None, func, &args));
+        result.to_rvalue()
+    }
+}
+
+impl<'gcc, 'tcx> HasCodegen<'tcx> for Builder<'_, 'gcc, 'tcx> {
+    type CodegenCx = CodegenCx<'gcc, 'tcx>;
+}
+
+impl<'tcx> HasTyCtxt<'tcx> for Builder<'_, '_, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.cx.tcx()
+    }
+}
+
+impl HasDataLayout for Builder<'_, '_, '_> {
+    fn data_layout(&self) -> &TargetDataLayout {
+        self.cx.data_layout()
+    }
+}
+
+impl<'tcx> LayoutOfHelpers<'tcx> for Builder<'_, '_, 'tcx> {
+    type LayoutOfResult = TyAndLayout<'tcx>;
+
+    #[inline]
+    fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
+        self.cx.handle_layout_err(err, span, ty)
+    }
+}
+
+impl<'tcx> FnAbiOfHelpers<'tcx> for Builder<'_, '_, 'tcx> {
+    type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>;
+
+    #[inline]
+    fn handle_fn_abi_err(
+        &self,
+        err: FnAbiError<'tcx>,
+        span: Span,
+        fn_abi_request: FnAbiRequest<'tcx>,
+    ) -> ! {
+        self.cx.handle_fn_abi_err(err, span, fn_abi_request)
+    }
+}
+
+impl<'gcc, 'tcx> Deref for Builder<'_, 'gcc, 'tcx> {
+    type Target = CodegenCx<'gcc, 'tcx>;
+
+    fn deref(&self) -> &Self::Target {
+        self.cx
+    }
+}
+
+impl<'gcc, 'tcx> BackendTypes for Builder<'_, 'gcc, 'tcx> {
+    type Value = <CodegenCx<'gcc, 'tcx> as BackendTypes>::Value;
+    type Function = <CodegenCx<'gcc, 'tcx> as BackendTypes>::Function;
+    type BasicBlock = <CodegenCx<'gcc, 'tcx> as BackendTypes>::BasicBlock;
+    type Type = <CodegenCx<'gcc, 'tcx> as BackendTypes>::Type;
+    type Funclet = <CodegenCx<'gcc, 'tcx> as BackendTypes>::Funclet;
+
+    type DIScope = <CodegenCx<'gcc, 'tcx> as BackendTypes>::DIScope;
+    type DILocation = <CodegenCx<'gcc, 'tcx> as BackendTypes>::DILocation;
+    type DIVariable = <CodegenCx<'gcc, 'tcx> as BackendTypes>::DIVariable;
+}
+
+impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
+    fn build(cx: &'a CodegenCx<'gcc, 'tcx>, block: Block<'gcc>) -> Self {
+        let mut bx = Builder::with_cx(cx);
+        *cx.current_block.borrow_mut() = Some(block);
+        bx.block = Some(block);
+        bx
+    }
+
+    fn build_sibling_block(&mut self, name: &str) -> Self {
+        let block = self.append_sibling_block(name);
+        Self::build(self.cx, block)
+    }
+
+    fn llbb(&self) -> Block<'gcc> {
+        self.block.expect("block")
+    }
+
+    fn append_block(cx: &'a CodegenCx<'gcc, 'tcx>, func: RValue<'gcc>, name: &str) -> Block<'gcc> {
+        let func = cx.rvalue_as_function(func);
+        func.new_block(name)
+    }
+
+    fn append_sibling_block(&mut self, name: &str) -> Block<'gcc> {
+        let func = self.current_func();
+        func.new_block(name)
+    }
+
+    fn ret_void(&mut self) {
+        self.llbb().end_with_void_return(None)
+    }
+
+    fn ret(&mut self, value: RValue<'gcc>) {
+        let value =
+            if self.structs_as_pointer.borrow().contains(&value) {
+                // NOTE: hack to workaround a limitation of the rustc API: see comment on
+                // CodegenCx.structs_as_pointer
+                value.dereference(None).to_rvalue()
+            }
+            else {
+                value
+            };
+        self.llbb().end_with_return(None, value);
+    }
+
+    fn br(&mut self, dest: Block<'gcc>) {
+        self.llbb().end_with_jump(None, dest)
+    }
+
+    fn cond_br(&mut self, cond: RValue<'gcc>, then_block: Block<'gcc>, else_block: Block<'gcc>) {
+        self.llbb().end_with_conditional(None, cond, then_block, else_block)
+    }
+
+    fn switch(&mut self, value: RValue<'gcc>, default_block: Block<'gcc>, cases: impl ExactSizeIterator<Item = (u128, Block<'gcc>)>) {
+        let mut gcc_cases = vec![];
+        let typ = self.val_ty(value);
+        for (on_val, dest) in cases {
+            let on_val = self.const_uint_big(typ, on_val);
+            gcc_cases.push(self.context.new_case(on_val, on_val, dest));
+        }
+        self.block.expect("block").end_with_switch(None, value, default_block, &gcc_cases);
+    }
+
+    fn invoke(&mut self, _typ: Type<'gcc>, _func: RValue<'gcc>, _args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
+        let condition = self.context.new_rvalue_from_int(self.bool_type, 0);
+        self.llbb().end_with_conditional(None, condition, then, catch);
+        self.context.new_rvalue_from_int(self.int_type, 0)
+
+        // TODO(antoyo)
+    }
+
+    fn unreachable(&mut self) {
+        let func = self.context.get_builtin_function("__builtin_unreachable");
+        let block = self.block.expect("block");
+        block.add_eval(None, self.context.new_call(None, func, &[]));
+        let return_type = block.get_function().get_return_type();
+        let void_type = self.context.new_type::<()>();
+        if return_type == void_type {
+            block.end_with_void_return(None)
+        }
+        else {
+            let return_value = self.current_func()
+                .new_local(None, return_type, "unreachableReturn");
+            block.end_with_return(None, return_value)
+        }
+    }
+
+    fn add(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
+        // FIXME(antoyo): this should not be required.
+        if format!("{:?}", a.get_type()) != format!("{:?}", b.get_type()) {
+            b = self.context.new_cast(None, b, a.get_type());
+        }
+        a + b
+    }
+
+    fn fadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a + b
+    }
+
+    fn sub(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
+        if a.get_type() != b.get_type() {
+            b = self.context.new_cast(None, b, a.get_type());
+        }
+        a - b
+    }
+
+    fn fsub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a - b
+    }
+
+    fn mul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a * b
+    }
+
+    fn fmul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a * b
+    }
+
+    fn udiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): convert the arguments to unsigned?
+        a / b
+    }
+
+    fn exactudiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): convert the arguments to unsigned?
+        // TODO(antoyo): poison if not exact.
+        a / b
+    }
+
+    fn sdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): convert the arguments to signed?
+        a / b
+    }
+
+    fn exactsdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): posion if not exact.
+        // FIXME(antoyo): rustc_codegen_ssa::mir::intrinsic uses different types for a and b but they
+        // should be the same.
+        let typ = a.get_type().to_signed(self);
+        let b = self.context.new_cast(None, b, typ);
+        a / b
+    }
+
+    fn fdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a / b
+    }
+
+    fn urem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a % b
+    }
+
+    fn srem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a % b
+    }
+
+    fn frem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        if a.get_type() == self.cx.float_type {
+            let fmodf = self.context.get_builtin_function("fmodf");
+            // FIXME(antoyo): this seems to produce the wrong result.
+            return self.context.new_call(None, fmodf, &[a, b]);
+        }
+        assert_eq!(a.get_type(), self.cx.double_type);
+
+        let fmod = self.context.get_builtin_function("fmod");
+        return self.context.new_call(None, fmod, &[a, b]);
+    }
+
+    fn shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
+        let a_type = a.get_type();
+        let b_type = b.get_type();
+        if a_type.is_unsigned(self) && b_type.is_signed(self) {
+            let a = self.context.new_cast(None, a, b_type);
+            let result = a << b;
+            self.context.new_cast(None, result, a_type)
+        }
+        else if a_type.is_signed(self) && b_type.is_unsigned(self) {
+            let b = self.context.new_cast(None, b, a_type);
+            a << b
+        }
+        else {
+            a << b
+        }
+    }
+
+    fn lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
+        // TODO(antoyo): cast to unsigned to do a logical shift if that does not work.
+        let a_type = a.get_type();
+        let b_type = b.get_type();
+        if a_type.is_unsigned(self) && b_type.is_signed(self) {
+            let a = self.context.new_cast(None, a, b_type);
+            let result = a >> b;
+            self.context.new_cast(None, result, a_type)
+        }
+        else if a_type.is_signed(self) && b_type.is_unsigned(self) {
+            let b = self.context.new_cast(None, b, a_type);
+            a >> b
+        }
+        else {
+            a >> b
+        }
+    }
+
+    fn ashr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): check whether behavior is an arithmetic shift for >> .
+        // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number.
+        let a_type = a.get_type();
+        let b_type = b.get_type();
+        if a_type.is_unsigned(self) && b_type.is_signed(self) {
+            let a = self.context.new_cast(None, a, b_type);
+            let result = a >> b;
+            self.context.new_cast(None, result, a_type)
+        }
+        else if a_type.is_signed(self) && b_type.is_unsigned(self) {
+            let b = self.context.new_cast(None, b, a_type);
+            a >> b
+        }
+        else {
+            a >> b
+        }
+    }
+
+    fn and(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> {
+        // FIXME(antoyo): hack by putting the result in a variable to workaround this bug:
+        // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=95498
+        if a.get_type() != b.get_type() {
+            b = self.context.new_cast(None, b, a.get_type());
+        }
+        let res = self.current_func().new_local(None, b.get_type(), "andResult");
+        self.llbb().add_assignment(None, res, a & b);
+        res.to_rvalue()
+    }
+
+    fn or(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        // FIXME(antoyo): hack by putting the result in a variable to workaround this bug:
+        // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=95498
+        let res = self.current_func().new_local(None, b.get_type(), "orResult");
+        self.llbb().add_assignment(None, res, a | b);
+        res.to_rvalue()
+    }
+
+    fn xor(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a ^ b
+    }
+
+    fn neg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): use new_unary_op()?
+        self.cx.context.new_rvalue_from_long(a.get_type(), 0) - a
+    }
+
+    fn fneg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
+        self.cx.context.new_unary_op(None, UnaryOp::Minus, a.get_type(), a)
+    }
+
+    fn not(&mut self, a: RValue<'gcc>) -> RValue<'gcc> {
+        let operation =
+            if a.get_type().is_bool() {
+                UnaryOp::LogicalNegate
+            }
+            else {
+                UnaryOp::BitwiseNegate
+            };
+        self.cx.context.new_unary_op(None, operation, a.get_type(), a)
+    }
+
+    fn unchecked_sadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a + b
+    }
+
+    fn unchecked_uadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a + b
+    }
+
+    fn unchecked_ssub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a - b
+    }
+
+    fn unchecked_usub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): should generate poison value?
+        a - b
+    }
+
+    fn unchecked_smul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a * b
+    }
+
+    fn unchecked_umul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> {
+        a * b
+    }
+
+    fn fadd_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn fsub_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn fmul_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn fdiv_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn frem_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn checked_binop(&mut self, oop: OverflowOp, typ: Ty<'_>, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) {
+        use rustc_middle::ty::{Int, IntTy::*, Uint, UintTy::*};
+
+        let new_kind =
+            match typ.kind() {
+                Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)),
+                Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)),
+                t @ (Uint(_) | Int(_)) => t.clone(),
+                _ => panic!("tried to get overflow intrinsic for op applied to non-int type"),
+            };
+
+        // TODO(antoyo): remove duplication with intrinsic?
+        let name =
+            match oop {
+                OverflowOp::Add =>
+                    match new_kind {
+                        Int(I8) => "__builtin_add_overflow",
+                        Int(I16) => "__builtin_add_overflow",
+                        Int(I32) => "__builtin_sadd_overflow",
+                        Int(I64) => "__builtin_saddll_overflow",
+                        Int(I128) => "__builtin_add_overflow",
+
+                        Uint(U8) => "__builtin_add_overflow",
+                        Uint(U16) => "__builtin_add_overflow",
+                        Uint(U32) => "__builtin_uadd_overflow",
+                        Uint(U64) => "__builtin_uaddll_overflow",
+                        Uint(U128) => "__builtin_add_overflow",
+
+                        _ => unreachable!(),
+                    },
+                OverflowOp::Sub =>
+                    match new_kind {
+                        Int(I8) => "__builtin_sub_overflow",
+                        Int(I16) => "__builtin_sub_overflow",
+                        Int(I32) => "__builtin_ssub_overflow",
+                        Int(I64) => "__builtin_ssubll_overflow",
+                        Int(I128) => "__builtin_sub_overflow",
+
+                        Uint(U8) => "__builtin_sub_overflow",
+                        Uint(U16) => "__builtin_sub_overflow",
+                        Uint(U32) => "__builtin_usub_overflow",
+                        Uint(U64) => "__builtin_usubll_overflow",
+                        Uint(U128) => "__builtin_sub_overflow",
+
+                        _ => unreachable!(),
+                    },
+                OverflowOp::Mul =>
+                    match new_kind {
+                        Int(I8) => "__builtin_mul_overflow",
+                        Int(I16) => "__builtin_mul_overflow",
+                        Int(I32) => "__builtin_smul_overflow",
+                        Int(I64) => "__builtin_smulll_overflow",
+                        Int(I128) => "__builtin_mul_overflow",
+
+                        Uint(U8) => "__builtin_mul_overflow",
+                        Uint(U16) => "__builtin_mul_overflow",
+                        Uint(U32) => "__builtin_umul_overflow",
+                        Uint(U64) => "__builtin_umulll_overflow",
+                        Uint(U128) => "__builtin_mul_overflow",
+
+                        _ => unreachable!(),
+                    },
+            };
+
+        let intrinsic = self.context.get_builtin_function(&name);
+        let res = self.current_func()
+            // TODO(antoyo): is it correct to use rhs type instead of the parameter typ?
+            .new_local(None, rhs.get_type(), "binopResult")
+            .get_address(None);
+        let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None);
+        (res.dereference(None).to_rvalue(), overflow)
+    }
+
+    fn alloca(&mut self, ty: Type<'gcc>, align: Align) -> RValue<'gcc> {
+        // FIXME(antoyo): this check that we don't call get_aligned() a second time on a type.
+        // Ideally, we shouldn't need to do this check.
+        let aligned_type =
+            if ty == self.cx.u128_type || ty == self.cx.i128_type {
+                ty
+            }
+            else {
+                ty.get_aligned(align.bytes())
+            };
+        // TODO(antoyo): It might be better to return a LValue, but fixing the rustc API is non-trivial.
+        self.stack_var_count.set(self.stack_var_count.get() + 1);
+        self.current_func().new_local(None, aligned_type, &format!("stack_var_{}", self.stack_var_count.get())).get_address(None)
+    }
+
+    fn dynamic_alloca(&mut self, _ty: Type<'gcc>, _align: Align) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn array_alloca(&mut self, _ty: Type<'gcc>, _len: RValue<'gcc>, _align: Align) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>, _align: Align) -> RValue<'gcc> {
+        // TODO(antoyo): use ty.
+        let block = self.llbb();
+        let function = block.get_function();
+        // NOTE: instead of returning the dereference here, we have to assign it to a variable in
+        // the current basic block. Otherwise, it could be used in another basic block, causing a
+        // dereference after a drop, for instance.
+        // TODO(antoyo): handle align.
+        let deref = ptr.dereference(None).to_rvalue();
+        let value_type = deref.get_type();
+        unsafe { RETURN_VALUE_COUNT += 1 };
+        let loaded_value = function.new_local(None, value_type, &format!("loadedValue{}", unsafe { RETURN_VALUE_COUNT }));
+        block.add_assignment(None, loaded_value, deref);
+        loaded_value.to_rvalue()
+    }
+
+    fn volatile_load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): use ty.
+        let ptr = self.context.new_cast(None, ptr, ptr.get_type().make_volatile());
+        ptr.dereference(None).to_rvalue()
+    }
+
+    fn atomic_load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>, order: AtomicOrdering, size: Size) -> RValue<'gcc> {
+        // TODO(antoyo): use ty.
+        // TODO(antoyo): handle alignment.
+        let atomic_load = self.context.get_builtin_function(&format!("__atomic_load_{}", size.bytes()));
+        let ordering = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
+
+        let volatile_const_void_ptr_type = self.context.new_type::<*mut ()>().make_const().make_volatile();
+        let ptr = self.context.new_cast(None, ptr, volatile_const_void_ptr_type);
+        self.context.new_call(None, atomic_load, &[ptr, ordering])
+    }
+
+    fn load_operand(&mut self, place: PlaceRef<'tcx, RValue<'gcc>>) -> OperandRef<'tcx, RValue<'gcc>> {
+        assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
+
+        if place.layout.is_zst() {
+            return OperandRef::new_zst(self, place.layout);
+        }
+
+        fn scalar_load_metadata<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, load: RValue<'gcc>, scalar: &abi::Scalar) {
+            let vr = scalar.valid_range.clone();
+            match scalar.value {
+                abi::Int(..) => {
+                    if !scalar.is_always_valid(bx) {
+                        bx.range_metadata(load, scalar.valid_range);
+                    }
+                }
+                abi::Pointer if vr.start < vr.end && !vr.contains(0) => {
+                    bx.nonnull_metadata(load);
+                }
+                _ => {}
+            }
+        }
+
+        let val =
+            if let Some(llextra) = place.llextra {
+                OperandValue::Ref(place.llval, Some(llextra), place.align)
+            }
+            else if place.layout.is_gcc_immediate() {
+                let load = self.load(place.llval.get_type(), place.llval, place.align);
+                if let abi::Abi::Scalar(ref scalar) = place.layout.abi {
+                    scalar_load_metadata(self, load, scalar);
+                }
+                OperandValue::Immediate(self.to_immediate(load, place.layout))
+            }
+            else if let abi::Abi::ScalarPair(ref a, ref b) = place.layout.abi {
+                let b_offset = a.value.size(self).align_to(b.value.align(self).abi);
+                let pair_type = place.layout.gcc_type(self, false);
+
+                let mut load = |i, scalar: &abi::Scalar, align| {
+                    let llptr = self.struct_gep(pair_type, place.llval, i as u64);
+                    let load = self.load(llptr.get_type(), llptr, align);
+                    scalar_load_metadata(self, load, scalar);
+                    if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load }
+                };
+
+                OperandValue::Pair(
+                    load(0, a, place.align),
+                    load(1, b, place.align.restrict_for_offset(b_offset)),
+                )
+            }
+            else {
+                OperandValue::Ref(place.llval, None, place.align)
+            };
+
+        OperandRef { val, layout: place.layout }
+    }
+
+    fn write_operand_repeatedly(mut self, cg_elem: OperandRef<'tcx, RValue<'gcc>>, count: u64, dest: PlaceRef<'tcx, RValue<'gcc>>) -> Self {
+        let zero = self.const_usize(0);
+        let count = self.const_usize(count);
+        let start = dest.project_index(&mut self, zero).llval;
+        let end = dest.project_index(&mut self, count).llval;
+
+        let mut header_bx = self.build_sibling_block("repeat_loop_header");
+        let mut body_bx = self.build_sibling_block("repeat_loop_body");
+        let next_bx = self.build_sibling_block("repeat_loop_next");
+
+        let ptr_type = start.get_type();
+        let current = self.llbb().get_function().new_local(None, ptr_type, "loop_var");
+        let current_val = current.to_rvalue();
+        self.assign(current, start);
+
+        self.br(header_bx.llbb());
+
+        let keep_going = header_bx.icmp(IntPredicate::IntNE, current_val, end);
+        header_bx.cond_br(keep_going, body_bx.llbb(), next_bx.llbb());
+
+        let align = dest.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size);
+        cg_elem.val.store(&mut body_bx, PlaceRef::new_sized_aligned(current_val, cg_elem.layout, align));
+
+        let next = body_bx.inbounds_gep(self.backend_type(cg_elem.layout), current.to_rvalue(), &[self.const_usize(1)]);
+        body_bx.llbb().add_assignment(None, current, next);
+        body_bx.br(header_bx.llbb());
+
+        next_bx
+    }
+
+    fn range_metadata(&mut self, _load: RValue<'gcc>, _range: WrappingRange) {
+        // TODO(antoyo)
+    }
+
+    fn nonnull_metadata(&mut self, _load: RValue<'gcc>) {
+        // TODO(antoyo)
+    }
+
+    fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> {
+        self.store_with_flags(val, ptr, align, MemFlags::empty())
+    }
+
+    fn store_with_flags(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, _align: Align, _flags: MemFlags) -> RValue<'gcc> {
+        let ptr = self.check_store(val, ptr);
+        self.llbb().add_assignment(None, ptr.dereference(None), val);
+        // TODO(antoyo): handle align and flags.
+        // NOTE: dummy value here since it's never used. FIXME(antoyo): API should not return a value here?
+        self.cx.context.new_rvalue_zero(self.type_i32())
+    }
+
+    fn atomic_store(&mut self, value: RValue<'gcc>, ptr: RValue<'gcc>, order: AtomicOrdering, size: Size) {
+        // TODO(antoyo): handle alignment.
+        let atomic_store = self.context.get_builtin_function(&format!("__atomic_store_{}", size.bytes()));
+        let ordering = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
+        let volatile_const_void_ptr_type = self.context.new_type::<*mut ()>().make_const().make_volatile();
+        let ptr = self.context.new_cast(None, ptr, volatile_const_void_ptr_type);
+
+        // FIXME(antoyo): fix libgccjit to allow comparing an integer type with an aligned integer type because
+        // the following cast is required to avoid this error:
+        // gcc_jit_context_new_call: mismatching types for argument 2 of function "__atomic_store_4": assignment to param arg1 (type: int) from loadedValue3577 (type: unsigned int  __attribute__((aligned(4))))
+        let int_type = atomic_store.get_param(1).to_rvalue().get_type();
+        let value = self.context.new_cast(None, value, int_type);
+        self.llbb()
+            .add_eval(None, self.context.new_call(None, atomic_store, &[ptr, value, ordering]));
+    }
+
+    fn gep(&mut self, _typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> {
+        let mut result = ptr;
+        for index in indices {
+            result = self.context.new_array_access(None, result, *index).get_address(None).to_rvalue();
+        }
+        result
+    }
+
+    fn inbounds_gep(&mut self, _typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> {
+        // FIXME(antoyo): would be safer if doing the same thing (loop) as gep.
+        // TODO(antoyo): specify inbounds somehow.
+        match indices.len() {
+            1 => {
+                self.context.new_array_access(None, ptr, indices[0]).get_address(None)
+            },
+            2 => {
+                let array = ptr.dereference(None); // TODO(antoyo): assert that first index is 0?
+                self.context.new_array_access(None, array, indices[1]).get_address(None)
+            },
+            _ => unimplemented!(),
+        }
+    }
+
+    fn struct_gep(&mut self, value_type: Type<'gcc>, ptr: RValue<'gcc>, idx: u64) -> RValue<'gcc> {
+        // FIXME(antoyo): it would be better if the API only called this on struct, not on arrays.
+        assert_eq!(idx as usize as u64, idx);
+        let value = ptr.dereference(None).to_rvalue();
+
+        if value_type.is_array().is_some() {
+            let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from"));
+            let element = self.context.new_array_access(None, value, index);
+            element.get_address(None)
+        }
+        else if let Some(vector_type) = value_type.is_vector() {
+            let array_type = vector_type.get_element_type().make_pointer();
+            let array = self.bitcast(ptr, array_type);
+            let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from"));
+            let element = self.context.new_array_access(None, array, index);
+            element.get_address(None)
+        }
+        else if let Some(struct_type) = value_type.is_struct() {
+            ptr.dereference_field(None, struct_type.get_field(idx as i32)).get_address(None)
+        }
+        else {
+            panic!("Unexpected type {:?}", value_type);
+        }
+    }
+
+    /* Casts */
+    fn trunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): check that it indeed truncate the value.
+        self.context.new_cast(None, value, dest_ty)
+    }
+
+    fn sext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): check that it indeed sign extend the value.
+        if dest_ty.is_vector().is_some() {
+            // TODO(antoyo): nothing to do as it is only for LLVM?
+            return value;
+        }
+        self.context.new_cast(None, value, dest_ty)
+    }
+
+    fn fptoui(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        self.context.new_cast(None, value, dest_ty)
+    }
+
+    fn fptosi(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        self.context.new_cast(None, value, dest_ty)
+    }
+
+    fn uitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        self.context.new_cast(None, value, dest_ty)
+    }
+
+    fn sitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        self.context.new_cast(None, value, dest_ty)
+    }
+
+    fn fptrunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): make sure it truncates.
+        self.context.new_cast(None, value, dest_ty)
+    }
+
+    fn fpext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        self.context.new_cast(None, value, dest_ty)
+    }
+
+    fn ptrtoint(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        self.cx.ptrtoint(self.block.expect("block"), value, dest_ty)
+    }
+
+    fn inttoptr(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        self.cx.inttoptr(self.block.expect("block"), value, dest_ty)
+    }
+
+    fn bitcast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        self.cx.const_bitcast(value, dest_ty)
+    }
+
+    fn intcast(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>, _is_signed: bool) -> RValue<'gcc> {
+        // NOTE: is_signed is for value, not dest_typ.
+        self.cx.context.new_cast(None, value, dest_typ)
+    }
+
+    fn pointercast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        let val_type = value.get_type();
+        match (type_is_pointer(val_type), type_is_pointer(dest_ty)) {
+            (false, true) => {
+                // NOTE: Projecting a field of a pointer type will attemp a cast from a signed char to
+                // a pointer, which is not supported by gccjit.
+                return self.cx.context.new_cast(None, self.inttoptr(value, val_type.make_pointer()), dest_ty);
+            },
+            (false, false) => {
+                // When they are not pointers, we want a transmute (or reinterpret_cast).
+                self.bitcast(value, dest_ty)
+            },
+            (true, true) => self.cx.context.new_cast(None, value, dest_ty),
+            (true, false) => unimplemented!(),
+        }
+    }
+
+    /* Comparisons */
+    fn icmp(&mut self, op: IntPredicate, mut lhs: RValue<'gcc>, mut rhs: RValue<'gcc>) -> RValue<'gcc> {
+        let left_type = lhs.get_type();
+        let right_type = rhs.get_type();
+        if left_type != right_type {
+            // NOTE: because libgccjit cannot compare function pointers.
+            if left_type.is_function_ptr_type().is_some() && right_type.is_function_ptr_type().is_some() {
+                lhs = self.context.new_cast(None, lhs, self.usize_type.make_pointer());
+                rhs = self.context.new_cast(None, rhs, self.usize_type.make_pointer());
+            }
+            // NOTE: hack because we try to cast a vector type to the same vector type.
+            else if format!("{:?}", left_type) != format!("{:?}", right_type) {
+                rhs = self.context.new_cast(None, rhs, left_type);
+            }
+        }
+        self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs)
+    }
+
+    fn fcmp(&mut self, op: RealPredicate, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
+        self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs)
+    }
+
+    /* Miscellaneous instructions */
+    fn memcpy(&mut self, dst: RValue<'gcc>, dst_align: Align, src: RValue<'gcc>, src_align: Align, size: RValue<'gcc>, flags: MemFlags) {
+        if flags.contains(MemFlags::NONTEMPORAL) {
+            // HACK(nox): This is inefficient but there is no nontemporal memcpy.
+            let val = self.load(src.get_type(), src, src_align);
+            let ptr = self.pointercast(dst, self.type_ptr_to(self.val_ty(val)));
+            self.store_with_flags(val, ptr, dst_align, flags);
+            return;
+        }
+        let size = self.intcast(size, self.type_size_t(), false);
+        let _is_volatile = flags.contains(MemFlags::VOLATILE);
+        let dst = self.pointercast(dst, self.type_i8p());
+        let src = self.pointercast(src, self.type_ptr_to(self.type_void()));
+        let memcpy = self.context.get_builtin_function("memcpy");
+        let block = self.block.expect("block");
+        // TODO(antoyo): handle aligns and is_volatile.
+        block.add_eval(None, self.context.new_call(None, memcpy, &[dst, src, size]));
+    }
+
+    fn memmove(&mut self, dst: RValue<'gcc>, dst_align: Align, src: RValue<'gcc>, src_align: Align, size: RValue<'gcc>, flags: MemFlags) {
+        if flags.contains(MemFlags::NONTEMPORAL) {
+            // HACK(nox): This is inefficient but there is no nontemporal memmove.
+            let val = self.load(src.get_type(), src, src_align);
+            let ptr = self.pointercast(dst, self.type_ptr_to(self.val_ty(val)));
+            self.store_with_flags(val, ptr, dst_align, flags);
+            return;
+        }
+        let size = self.intcast(size, self.type_size_t(), false);
+        let _is_volatile = flags.contains(MemFlags::VOLATILE);
+        let dst = self.pointercast(dst, self.type_i8p());
+        let src = self.pointercast(src, self.type_ptr_to(self.type_void()));
+
+        let memmove = self.context.get_builtin_function("memmove");
+        let block = self.block.expect("block");
+        // TODO(antoyo): handle is_volatile.
+        block.add_eval(None, self.context.new_call(None, memmove, &[dst, src, size]));
+    }
+
+    fn memset(&mut self, ptr: RValue<'gcc>, fill_byte: RValue<'gcc>, size: RValue<'gcc>, _align: Align, flags: MemFlags) {
+        let _is_volatile = flags.contains(MemFlags::VOLATILE);
+        let ptr = self.pointercast(ptr, self.type_i8p());
+        let memset = self.context.get_builtin_function("memset");
+        let block = self.block.expect("block");
+        // TODO(antoyo): handle align and is_volatile.
+        let fill_byte = self.context.new_cast(None, fill_byte, self.i32_type);
+        let size = self.intcast(size, self.type_size_t(), false);
+        block.add_eval(None, self.context.new_call(None, memset, &[ptr, fill_byte, size]));
+    }
+
+    fn select(&mut self, cond: RValue<'gcc>, then_val: RValue<'gcc>, mut else_val: RValue<'gcc>) -> RValue<'gcc> {
+        let func = self.current_func();
+        let variable = func.new_local(None, then_val.get_type(), "selectVar");
+        let then_block = func.new_block("then");
+        let else_block = func.new_block("else");
+        let after_block = func.new_block("after");
+        self.llbb().end_with_conditional(None, cond, then_block, else_block);
+
+        then_block.add_assignment(None, variable, then_val);
+        then_block.end_with_jump(None, after_block);
+
+        if then_val.get_type() != else_val.get_type() {
+            else_val = self.context.new_cast(None, else_val, then_val.get_type());
+        }
+        else_block.add_assignment(None, variable, else_val);
+        else_block.end_with_jump(None, after_block);
+
+        // NOTE: since jumps were added in a place rustc does not expect, the current blocks in the
+        // state need to be updated.
+        self.block = Some(after_block);
+        *self.cx.current_block.borrow_mut() = Some(after_block);
+
+        variable.to_rvalue()
+    }
+
+    #[allow(dead_code)]
+    fn va_arg(&mut self, _list: RValue<'gcc>, _ty: Type<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn extract_element(&mut self, _vec: RValue<'gcc>, _idx: RValue<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn vector_splat(&mut self, _num_elts: usize, _elt: RValue<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn extract_value(&mut self, aggregate_value: RValue<'gcc>, idx: u64) -> RValue<'gcc> {
+        // FIXME(antoyo): it would be better if the API only called this on struct, not on arrays.
+        assert_eq!(idx as usize as u64, idx);
+        let value_type = aggregate_value.get_type();
+
+        if value_type.is_array().is_some() {
+            let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from"));
+            let element = self.context.new_array_access(None, aggregate_value, index);
+            element.get_address(None)
+        }
+        else if value_type.is_vector().is_some() {
+            panic!();
+        }
+        else if let Some(pointer_type) = value_type.get_pointee() {
+            if let Some(struct_type) = pointer_type.is_struct() {
+                // NOTE: hack to workaround a limitation of the rustc API: see comment on
+                // CodegenCx.structs_as_pointer
+                aggregate_value.dereference_field(None, struct_type.get_field(idx as i32)).to_rvalue()
+            }
+            else {
+                panic!("Unexpected type {:?}", value_type);
+            }
+        }
+        else if let Some(struct_type) = value_type.is_struct() {
+            aggregate_value.access_field(None, struct_type.get_field(idx as i32)).to_rvalue()
+        }
+        else {
+            panic!("Unexpected type {:?}", value_type);
+        }
+    }
+
+    fn insert_value(&mut self, aggregate_value: RValue<'gcc>, value: RValue<'gcc>, idx: u64) -> RValue<'gcc> {
+        // FIXME(antoyo): it would be better if the API only called this on struct, not on arrays.
+        assert_eq!(idx as usize as u64, idx);
+        let value_type = aggregate_value.get_type();
+
+        let lvalue =
+            if value_type.is_array().is_some() {
+                let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from"));
+                self.context.new_array_access(None, aggregate_value, index)
+            }
+            else if value_type.is_vector().is_some() {
+                panic!();
+            }
+            else if let Some(pointer_type) = value_type.get_pointee() {
+                if let Some(struct_type) = pointer_type.is_struct() {
+                    // NOTE: hack to workaround a limitation of the rustc API: see comment on
+                    // CodegenCx.structs_as_pointer
+                    aggregate_value.dereference_field(None, struct_type.get_field(idx as i32))
+                }
+                else {
+                    panic!("Unexpected type {:?}", value_type);
+                }
+            }
+            else {
+                panic!("Unexpected type {:?}", value_type);
+            };
+
+        let lvalue_type = lvalue.to_rvalue().get_type();
+        let value =
+            // NOTE: sometimes, rustc will create a value with the wrong type.
+            if lvalue_type != value.get_type() {
+                self.context.new_cast(None, value, lvalue_type)
+            }
+            else {
+                value
+            };
+
+        self.llbb().add_assignment(None, lvalue, value);
+
+        aggregate_value
+    }
+
+    fn landing_pad(&mut self, _ty: Type<'gcc>, _pers_fn: RValue<'gcc>, _num_clauses: usize) -> RValue<'gcc> {
+        let field1 = self.context.new_field(None, self.u8_type, "landing_pad_field_1");
+        let field2 = self.context.new_field(None, self.i32_type, "landing_pad_field_1");
+        let struct_type = self.context.new_struct_type(None, "landing_pad", &[field1, field2]);
+        self.current_func().new_local(None, struct_type.as_type(), "landing_pad")
+            .to_rvalue()
+        // TODO(antoyo): Properly implement unwinding.
+        // the above is just to make the compilation work as it seems
+        // rustc_codegen_ssa now calls the unwinding builder methods even on panic=abort.
+    }
+
+    fn set_cleanup(&mut self, _landing_pad: RValue<'gcc>) {
+        // TODO(antoyo)
+    }
+
+    fn resume(&mut self, _exn: RValue<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn cleanup_pad(&mut self, _parent: Option<RValue<'gcc>>, _args: &[RValue<'gcc>]) -> Funclet {
+        unimplemented!();
+    }
+
+    fn cleanup_ret(&mut self, _funclet: &Funclet, _unwind: Option<Block<'gcc>>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn catch_pad(&mut self, _parent: RValue<'gcc>, _args: &[RValue<'gcc>]) -> Funclet {
+        unimplemented!();
+    }
+
+    fn catch_switch(&mut self, _parent: Option<RValue<'gcc>>, _unwind: Option<Block<'gcc>>, _num_handlers: usize) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn add_handler(&mut self, _catch_switch: RValue<'gcc>, _handler: Block<'gcc>) {
+        unimplemented!();
+    }
+
+    fn set_personality_fn(&mut self, _personality: RValue<'gcc>) {
+        // TODO(antoyo)
+    }
+
+    // Atomic Operations
+    fn atomic_cmpxchg(&mut self, dst: RValue<'gcc>, cmp: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> {
+        let expected = self.current_func().new_local(None, cmp.get_type(), "expected");
+        self.llbb().add_assignment(None, expected, cmp);
+        let success = self.compare_exchange(dst, expected, src, order, failure_order, weak);
+
+        let pair_type = self.cx.type_struct(&[src.get_type(), self.bool_type], false);
+        let result = self.current_func().new_local(None, pair_type, "atomic_cmpxchg_result");
+        let align = Align::from_bits(64).expect("align"); // TODO(antoyo): use good align.
+
+        let value_type = result.to_rvalue().get_type();
+        if let Some(struct_type) = value_type.is_struct() {
+            self.store(success, result.access_field(None, struct_type.get_field(1)).get_address(None), align);
+            // NOTE: since success contains the call to the intrinsic, it must be stored before
+            // expected so that we store expected after the call.
+            self.store(expected.to_rvalue(), result.access_field(None, struct_type.get_field(0)).get_address(None), align);
+        }
+        // TODO(antoyo): handle when value is not a struct.
+
+        result.to_rvalue()
+    }
+
+    fn atomic_rmw(&mut self, op: AtomicRmwBinOp, dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering) -> RValue<'gcc> {
+        let size = self.cx.int_width(src.get_type()) / 8;
+        let name =
+            match op {
+                AtomicRmwBinOp::AtomicXchg => format!("__atomic_exchange_{}", size),
+                AtomicRmwBinOp::AtomicAdd => format!("__atomic_fetch_add_{}", size),
+                AtomicRmwBinOp::AtomicSub => format!("__atomic_fetch_sub_{}", size),
+                AtomicRmwBinOp::AtomicAnd => format!("__atomic_fetch_and_{}", size),
+                AtomicRmwBinOp::AtomicNand => format!("__atomic_fetch_nand_{}", size),
+                AtomicRmwBinOp::AtomicOr => format!("__atomic_fetch_or_{}", size),
+                AtomicRmwBinOp::AtomicXor => format!("__atomic_fetch_xor_{}", size),
+                AtomicRmwBinOp::AtomicMax => return self.atomic_extremum(ExtremumOperation::Max, dst, src, order),
+                AtomicRmwBinOp::AtomicMin => return self.atomic_extremum(ExtremumOperation::Min, dst, src, order),
+                AtomicRmwBinOp::AtomicUMax => return self.atomic_extremum(ExtremumOperation::Max, dst, src, order),
+                AtomicRmwBinOp::AtomicUMin => return self.atomic_extremum(ExtremumOperation::Min, dst, src, order),
+            };
+
+
+        let atomic_function = self.context.get_builtin_function(name);
+        let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
+
+        let void_ptr_type = self.context.new_type::<*mut ()>();
+        let volatile_void_ptr_type = void_ptr_type.make_volatile();
+        let dst = self.context.new_cast(None, dst, volatile_void_ptr_type);
+        // FIXME(antoyo): not sure why, but we have the wrong type here.
+        let new_src_type = atomic_function.get_param(1).to_rvalue().get_type();
+        let src = self.context.new_cast(None, src, new_src_type);
+        let res = self.context.new_call(None, atomic_function, &[dst, src, order]);
+        self.context.new_cast(None, res, src.get_type())
+    }
+
+    fn atomic_fence(&mut self, order: AtomicOrdering, scope: SynchronizationScope) {
+        let name =
+            match scope {
+                SynchronizationScope::SingleThread => "__atomic_signal_fence",
+                SynchronizationScope::CrossThread => "__atomic_thread_fence",
+            };
+        let thread_fence = self.context.get_builtin_function(name);
+        let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc());
+        self.llbb().add_eval(None, self.context.new_call(None, thread_fence, &[order]));
+    }
+
+    fn set_invariant_load(&mut self, load: RValue<'gcc>) {
+        // NOTE: Hack to consider vtable function pointer as non-global-variable function pointer.
+        self.normal_function_addresses.borrow_mut().insert(load);
+        // TODO(antoyo)
+    }
+
+    fn lifetime_start(&mut self, _ptr: RValue<'gcc>, _size: Size) {
+        // TODO(antoyo)
+    }
+
+    fn lifetime_end(&mut self, _ptr: RValue<'gcc>, _size: Size) {
+        // TODO(antoyo)
+    }
+
+    fn call(&mut self, _typ: Type<'gcc>, func: RValue<'gcc>, args: &[RValue<'gcc>], funclet: Option<&Funclet>) -> RValue<'gcc> {
+        // FIXME(antoyo): remove when having a proper API.
+        let gcc_func = unsafe { std::mem::transmute(func) };
+        if self.functions.borrow().values().find(|value| **value == gcc_func).is_some() {
+            self.function_call(func, args, funclet)
+        }
+        else {
+            // If it's a not function that was defined, it's a function pointer.
+            self.function_ptr_call(func, args, funclet)
+        }
+    }
+
+    fn zext(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> {
+        // FIXME(antoyo): this does not zero-extend.
+        if value.get_type().is_bool() && dest_typ.is_i8(&self.cx) {
+            // FIXME(antoyo): hack because base::from_immediate converts i1 to i8.
+            // Fix the code in codegen_ssa::base::from_immediate.
+            return value;
+        }
+        self.context.new_cast(None, value, dest_typ)
+    }
+
+    fn cx(&self) -> &CodegenCx<'gcc, 'tcx> {
+        self.cx
+    }
+
+    fn do_not_inline(&mut self, _llret: RValue<'gcc>) {
+        unimplemented!();
+    }
+
+    fn set_span(&mut self, _span: Span) {}
+
+    fn from_immediate(&mut self, val: Self::Value) -> Self::Value {
+        if self.cx().val_ty(val) == self.cx().type_i1() {
+            self.zext(val, self.cx().type_i8())
+        }
+        else {
+            val
+        }
+    }
+
+    fn to_immediate_scalar(&mut self, val: Self::Value, scalar: abi::Scalar) -> Self::Value {
+        if scalar.is_bool() {
+            return self.trunc(val, self.cx().type_i1());
+        }
+        val
+    }
+
+    fn fptoui_sat(&mut self, _val: RValue<'gcc>, _dest_ty: Type<'gcc>) -> Option<RValue<'gcc>> {
+        None
+    }
+
+    fn fptosi_sat(&mut self, _val: RValue<'gcc>, _dest_ty: Type<'gcc>) -> Option<RValue<'gcc>> {
+        None
+    }
+
+    fn instrprof_increment(&mut self, _fn_name: RValue<'gcc>, _hash: RValue<'gcc>, _num_counters: RValue<'gcc>, _index: RValue<'gcc>) {
+        unimplemented!();
+    }
+}
+
+impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
+    pub fn shuffle_vector(&mut self, v1: RValue<'gcc>, v2: RValue<'gcc>, mask: RValue<'gcc>) -> RValue<'gcc> {
+        let return_type = v1.get_type();
+        let params = [
+            self.context.new_parameter(None, return_type, "v1"),
+            self.context.new_parameter(None, return_type, "v2"),
+            self.context.new_parameter(None, mask.get_type(), "mask"),
+        ];
+        let shuffle = self.context.new_function(None, FunctionType::Extern, return_type, &params, "_mm_shuffle_epi8", false);
+        self.context.new_call(None, shuffle, &[v1, v2, mask])
+    }
+}
+
+impl<'a, 'gcc, 'tcx> StaticBuilderMethods for Builder<'a, 'gcc, 'tcx> {
+    fn get_static(&mut self, def_id: DefId) -> RValue<'gcc> {
+        // Forward to the `get_static` method of `CodegenCx`
+        self.cx().get_static(def_id).get_address(None)
+    }
+}
+
+impl<'tcx> HasParamEnv<'tcx> for Builder<'_, '_, 'tcx> {
+    fn param_env(&self) -> ParamEnv<'tcx> {
+        self.cx.param_env()
+    }
+}
+
+impl<'tcx> HasTargetSpec for Builder<'_, '_, 'tcx> {
+    fn target_spec(&self) -> &Target {
+        &self.cx.target_spec()
+    }
+}
+
+trait ToGccComp {
+    fn to_gcc_comparison(&self) -> ComparisonOp;
+}
+
+impl ToGccComp for IntPredicate {
+    fn to_gcc_comparison(&self) -> ComparisonOp {
+        match *self {
+            IntPredicate::IntEQ => ComparisonOp::Equals,
+            IntPredicate::IntNE => ComparisonOp::NotEquals,
+            IntPredicate::IntUGT => ComparisonOp::GreaterThan,
+            IntPredicate::IntUGE => ComparisonOp::GreaterThanEquals,
+            IntPredicate::IntULT => ComparisonOp::LessThan,
+            IntPredicate::IntULE => ComparisonOp::LessThanEquals,
+            IntPredicate::IntSGT => ComparisonOp::GreaterThan,
+            IntPredicate::IntSGE => ComparisonOp::GreaterThanEquals,
+            IntPredicate::IntSLT => ComparisonOp::LessThan,
+            IntPredicate::IntSLE => ComparisonOp::LessThanEquals,
+        }
+    }
+}
+
+impl ToGccComp for RealPredicate {
+    fn to_gcc_comparison(&self) -> ComparisonOp {
+        // TODO(antoyo): check that ordered vs non-ordered is respected.
+        match *self {
+            RealPredicate::RealPredicateFalse => unreachable!(),
+            RealPredicate::RealOEQ => ComparisonOp::Equals,
+            RealPredicate::RealOGT => ComparisonOp::GreaterThan,
+            RealPredicate::RealOGE => ComparisonOp::GreaterThanEquals,
+            RealPredicate::RealOLT => ComparisonOp::LessThan,
+            RealPredicate::RealOLE => ComparisonOp::LessThanEquals,
+            RealPredicate::RealONE => ComparisonOp::NotEquals,
+            RealPredicate::RealORD => unreachable!(),
+            RealPredicate::RealUNO => unreachable!(),
+            RealPredicate::RealUEQ => ComparisonOp::Equals,
+            RealPredicate::RealUGT => ComparisonOp::GreaterThan,
+            RealPredicate::RealUGE => ComparisonOp::GreaterThan,
+            RealPredicate::RealULT => ComparisonOp::LessThan,
+            RealPredicate::RealULE => ComparisonOp::LessThan,
+            RealPredicate::RealUNE => ComparisonOp::NotEquals,
+            RealPredicate::RealPredicateTrue => unreachable!(),
+        }
+    }
+}
+
+#[repr(C)]
+#[allow(non_camel_case_types)]
+enum MemOrdering {
+    __ATOMIC_RELAXED,
+    __ATOMIC_CONSUME,
+    __ATOMIC_ACQUIRE,
+    __ATOMIC_RELEASE,
+    __ATOMIC_ACQ_REL,
+    __ATOMIC_SEQ_CST,
+}
+
+trait ToGccOrdering {
+    fn to_gcc(self) -> i32;
+}
+
+impl ToGccOrdering for AtomicOrdering {
+    fn to_gcc(self) -> i32 {
+        use MemOrdering::*;
+
+        let ordering =
+            match self {
+                AtomicOrdering::NotAtomic => __ATOMIC_RELAXED, // TODO(antoyo): check if that's the same.
+                AtomicOrdering::Unordered => __ATOMIC_RELAXED,
+                AtomicOrdering::Monotonic => __ATOMIC_RELAXED, // TODO(antoyo): check if that's the same.
+                AtomicOrdering::Acquire => __ATOMIC_ACQUIRE,
+                AtomicOrdering::Release => __ATOMIC_RELEASE,
+                AtomicOrdering::AcquireRelease => __ATOMIC_ACQ_REL,
+                AtomicOrdering::SequentiallyConsistent => __ATOMIC_SEQ_CST,
+            };
+        ordering as i32
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs
new file mode 100644 (file)
index 0000000..76419b1
--- /dev/null
@@ -0,0 +1,77 @@
+use gccjit::{FunctionType, RValue};
+use rustc_codegen_ssa::traits::BaseTypeMethods;
+use rustc_middle::ty::{self, Instance, TypeFoldable};
+use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
+
+use crate::abi::FnAbiGccExt;
+use crate::context::CodegenCx;
+
+/// Codegens a reference to a fn/method item, monomorphizing and
+/// inlining as it goes.
+///
+/// # Parameters
+///
+/// - `cx`: the crate context
+/// - `instance`: the instance to be instantiated
+pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) -> RValue<'gcc> {
+    let tcx = cx.tcx();
+
+    assert!(!instance.substs.needs_infer());
+    assert!(!instance.substs.has_escaping_bound_vars());
+
+    if let Some(&func) = cx.function_instances.borrow().get(&instance) {
+        return func;
+    }
+
+    let sym = tcx.symbol_name(instance).name;
+
+    let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
+
+    let func =
+        if let Some(func) = cx.get_declared_value(&sym) {
+            // Create a fn pointer with the new signature.
+            let ptrty = fn_abi.ptr_to_gcc_type(cx);
+
+            // This is subtle and surprising, but sometimes we have to bitcast
+            // the resulting fn pointer.  The reason has to do with external
+            // functions.  If you have two crates that both bind the same C
+            // library, they may not use precisely the same types: for
+            // example, they will probably each declare their own structs,
+            // which are distinct types from LLVM's point of view (nominal
+            // types).
+            //
+            // Now, if those two crates are linked into an application, and
+            // they contain inlined code, you can wind up with a situation
+            // where both of those functions wind up being loaded into this
+            // application simultaneously. In that case, the same function
+            // (from LLVM's point of view) requires two types. But of course
+            // LLVM won't allow one function to have two types.
+            //
+            // What we currently do, therefore, is declare the function with
+            // one of the two types (whichever happens to come first) and then
+            // bitcast as needed when the function is referenced to make sure
+            // it has the type we expect.
+            //
+            // This can occur on either a crate-local or crate-external
+            // reference. It also occurs when testing libcore and in some
+            // other weird situations. Annoying.
+            if cx.val_ty(func) != ptrty {
+                // TODO(antoyo): cast the pointer.
+                func
+            }
+            else {
+                func
+            }
+        }
+        else {
+            cx.linkage.set(FunctionType::Extern);
+            let func = cx.declare_fn(&sym, &fn_abi);
+
+            // TODO(antoyo): set linkage and attributes.
+            func
+        };
+
+    cx.function_instances.borrow_mut().insert(instance, func);
+
+    func
+}
diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
new file mode 100644 (file)
index 0000000..bda08b6
--- /dev/null
@@ -0,0 +1,450 @@
+use std::convert::TryFrom;
+use std::convert::TryInto;
+
+use gccjit::LValue;
+use gccjit::{Block, CType, RValue, Type, ToRValue};
+use rustc_codegen_ssa::mir::place::PlaceRef;
+use rustc_codegen_ssa::traits::{
+    BaseTypeMethods,
+    ConstMethods,
+    DerivedTypeMethods,
+    MiscMethods,
+    StaticMethods,
+};
+use rustc_middle::mir::Mutability;
+use rustc_middle::ty::ScalarInt;
+use rustc_middle::ty::layout::{TyAndLayout, LayoutOf};
+use rustc_middle::mir::interpret::{Allocation, GlobalAlloc, Scalar};
+use rustc_span::Symbol;
+use rustc_target::abi::{self, HasDataLayout, Pointer, Size};
+
+use crate::consts::const_alloc_to_gcc;
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+    pub fn const_bytes(&self, bytes: &[u8]) -> RValue<'gcc> {
+        bytes_in_context(self, bytes)
+    }
+
+    fn const_cstr(&self, symbol: Symbol, _null_terminated: bool) -> LValue<'gcc> {
+        // TODO(antoyo): handle null_terminated.
+        if let Some(&value) = self.const_cstr_cache.borrow().get(&symbol) {
+            return value;
+        }
+
+        let global = self.global_string(&*symbol.as_str());
+
+        self.const_cstr_cache.borrow_mut().insert(symbol, global);
+        global
+    }
+
+    fn global_string(&self, string: &str) -> LValue<'gcc> {
+        // TODO(antoyo): handle non-null-terminated strings.
+        let string = self.context.new_string_literal(&*string);
+        let sym = self.generate_local_symbol_name("str");
+        let global = self.declare_private_global(&sym, self.val_ty(string));
+        global.global_set_initializer_value(string);
+        global
+        // TODO(antoyo): set linkage.
+    }
+
+    pub fn inttoptr(&self, block: Block<'gcc>, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        let func = block.get_function();
+        let local = func.new_local(None, value.get_type(), "intLocal");
+        block.add_assignment(None, local, value);
+        let value_address = local.get_address(None);
+
+        let ptr = self.context.new_cast(None, value_address, dest_ty.make_pointer());
+        ptr.dereference(None).to_rvalue()
+    }
+
+    pub fn ptrtoint(&self, block: Block<'gcc>, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): when libgccjit allow casting from pointer to int, remove this.
+        let func = block.get_function();
+        let local = func.new_local(None, value.get_type(), "ptrLocal");
+        block.add_assignment(None, local, value);
+        let ptr_address = local.get_address(None);
+
+        let ptr = self.context.new_cast(None, ptr_address, dest_ty.make_pointer());
+        ptr.dereference(None).to_rvalue()
+    }
+}
+
+pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> {
+    let context = &cx.context;
+    let byte_type = context.new_type::<u8>();
+    let typ = context.new_array_type(None, byte_type, bytes.len() as i32);
+    let elements: Vec<_> =
+        bytes.iter()
+        .map(|&byte| context.new_rvalue_from_int(byte_type, byte as i32))
+        .collect();
+    context.new_rvalue_from_array(None, typ, &elements)
+}
+
+pub fn type_is_pointer<'gcc>(typ: Type<'gcc>) -> bool {
+    typ.get_pointee().is_some()
+}
+
+impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn const_null(&self, typ: Type<'gcc>) -> RValue<'gcc> {
+        if type_is_pointer(typ) {
+            self.context.new_null(typ)
+        }
+        else {
+            self.const_int(typ, 0)
+        }
+    }
+
+    fn const_undef(&self, typ: Type<'gcc>) -> RValue<'gcc> {
+        let local = self.current_func.borrow().expect("func")
+            .new_local(None, typ, "undefined");
+        if typ.is_struct().is_some() {
+            // NOTE: hack to workaround a limitation of the rustc API: see comment on
+            // CodegenCx.structs_as_pointer
+            let pointer = local.get_address(None);
+            self.structs_as_pointer.borrow_mut().insert(pointer);
+            pointer
+        }
+        else {
+            local.to_rvalue()
+        }
+    }
+
+    fn const_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> {
+        self.context.new_rvalue_from_long(typ, i64::try_from(int).expect("i64::try_from"))
+    }
+
+    fn const_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> {
+        self.context.new_rvalue_from_long(typ, u64::try_from(int).expect("u64::try_from") as i64)
+    }
+
+    fn const_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> {
+        let num64: Result<i64, _> = num.try_into();
+        if let Ok(num) = num64 {
+            // FIXME(antoyo): workaround for a bug where libgccjit is expecting a constant.
+            // The operations >> 64 and | low are making the normal case a non-constant.
+            return self.context.new_rvalue_from_long(typ, num as i64);
+        }
+
+        if num >> 64 != 0 {
+            // FIXME(antoyo): use a new function new_rvalue_from_unsigned_long()?
+            let low = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64);
+            let high = self.context.new_rvalue_from_long(typ, (num >> 64) as u64 as i64);
+
+            let sixty_four = self.context.new_rvalue_from_long(typ, 64);
+            (high << sixty_four) | self.context.new_cast(None, low, typ)
+        }
+        else if typ.is_i128(self) {
+            let num = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64);
+            self.context.new_cast(None, num, typ)
+        }
+        else {
+            self.context.new_rvalue_from_long(typ, num as u64 as i64)
+        }
+    }
+
+    fn const_bool(&self, val: bool) -> RValue<'gcc> {
+        self.const_uint(self.type_i1(), val as u64)
+    }
+
+    fn const_i32(&self, i: i32) -> RValue<'gcc> {
+        self.const_int(self.type_i32(), i as i64)
+    }
+
+    fn const_u32(&self, i: u32) -> RValue<'gcc> {
+        self.const_uint(self.type_u32(), i as u64)
+    }
+
+    fn const_u64(&self, i: u64) -> RValue<'gcc> {
+        self.const_uint(self.type_u64(), i)
+    }
+
+    fn const_usize(&self, i: u64) -> RValue<'gcc> {
+        let bit_size = self.data_layout().pointer_size.bits();
+        if bit_size < 64 {
+            // make sure it doesn't overflow
+            assert!(i < (1 << bit_size));
+        }
+
+        self.const_uint(self.usize_type, i)
+    }
+
+    fn const_u8(&self, _i: u8) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn const_real(&self, _t: Type<'gcc>, _val: f64) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn const_str(&self, s: Symbol) -> (RValue<'gcc>, RValue<'gcc>) {
+        let len = s.as_str().len();
+        let cs = self.const_ptrcast(self.const_cstr(s, false).get_address(None),
+            self.type_ptr_to(self.layout_of(self.tcx.types.str_).gcc_type(self, true)),
+        );
+        (cs, self.const_usize(len as u64))
+    }
+
+    fn const_struct(&self, values: &[RValue<'gcc>], packed: bool) -> RValue<'gcc> {
+        let fields: Vec<_> = values.iter()
+            .map(|value| value.get_type())
+            .collect();
+        // TODO(antoyo): cache the type? It's anonymous, so probably not.
+        let typ = self.type_struct(&fields, packed);
+        let struct_type = typ.is_struct().expect("struct type");
+        self.context.new_rvalue_from_struct(None, struct_type, values)
+    }
+
+    fn const_to_opt_uint(&self, _v: RValue<'gcc>) -> Option<u64> {
+        // TODO(antoyo)
+        None
+    }
+
+    fn const_to_opt_u128(&self, _v: RValue<'gcc>, _sign_ext: bool) -> Option<u128> {
+        // TODO(antoyo)
+        None
+    }
+
+    fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>) -> RValue<'gcc> {
+        let bitsize = if layout.is_bool() { 1 } else { layout.value.size(self).bits() };
+        match cv {
+            Scalar::Int(ScalarInt::ZST) => {
+                assert_eq!(0, layout.value.size(self).bytes());
+                self.const_undef(self.type_ix(0))
+            }
+            Scalar::Int(int) => {
+                let data = int.assert_bits(layout.value.size(self));
+
+                // FIXME(antoyo): there's some issues with using the u128 code that follows, so hard-code
+                // the paths for floating-point values.
+                if ty == self.float_type {
+                    return self.context.new_rvalue_from_double(ty, f32::from_bits(data as u32) as f64);
+                }
+                else if ty == self.double_type {
+                    return self.context.new_rvalue_from_double(ty, f64::from_bits(data as u64));
+                }
+
+                let value = self.const_uint_big(self.type_ix(bitsize), data);
+                if layout.value == Pointer {
+                    self.inttoptr(self.current_block.borrow().expect("block"), value, ty)
+                } else {
+                    self.const_bitcast(value, ty)
+                }
+            }
+            Scalar::Ptr(ptr, _size) => {
+                let (alloc_id, offset) = ptr.into_parts();
+                let base_addr =
+                    match self.tcx.global_alloc(alloc_id) {
+                        GlobalAlloc::Memory(alloc) => {
+                            let init = const_alloc_to_gcc(self, alloc);
+                            let value =
+                                match alloc.mutability {
+                                    Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None),
+                                    _ => self.static_addr_of(init, alloc.align, None),
+                                };
+                            if !self.sess().fewer_names() {
+                                // TODO(antoyo): set value name.
+                            }
+                            value
+                        },
+                        GlobalAlloc::Function(fn_instance) => {
+                            self.get_fn_addr(fn_instance)
+                        },
+                        GlobalAlloc::Static(def_id) => {
+                            assert!(self.tcx.is_static(def_id));
+                            self.get_static(def_id).get_address(None)
+                        },
+                    };
+                let ptr_type = base_addr.get_type();
+                let base_addr = self.const_bitcast(base_addr, self.usize_type);
+                let offset = self.context.new_rvalue_from_long(self.usize_type, offset.bytes() as i64);
+                let ptr = self.const_bitcast(base_addr + offset, ptr_type);
+                if layout.value != Pointer {
+                    self.const_bitcast(ptr.dereference(None).to_rvalue(), ty)
+                }
+                else {
+                    self.const_bitcast(ptr, ty)
+                }
+            }
+        }
+    }
+
+    fn const_data_from_alloc(&self, alloc: &Allocation) -> Self::Value {
+        const_alloc_to_gcc(self, alloc)
+    }
+
+    fn from_const_alloc(&self, layout: TyAndLayout<'tcx>, alloc: &Allocation, offset: Size) -> PlaceRef<'tcx, RValue<'gcc>> {
+        assert_eq!(alloc.align, layout.align.abi);
+        let ty = self.type_ptr_to(layout.gcc_type(self, true));
+        let value =
+            if layout.size == Size::ZERO {
+                let value = self.const_usize(alloc.align.bytes());
+                self.context.new_cast(None, value, ty)
+            }
+            else {
+                let init = const_alloc_to_gcc(self, alloc);
+                let base_addr = self.static_addr_of(init, alloc.align, None);
+
+                let array = self.const_bitcast(base_addr, self.type_i8p());
+                let value = self.context.new_array_access(None, array, self.const_usize(offset.bytes())).get_address(None);
+                self.const_bitcast(value, ty)
+            };
+        PlaceRef::new_sized(value, layout)
+    }
+
+    fn const_ptrcast(&self, val: RValue<'gcc>, ty: Type<'gcc>) -> RValue<'gcc> {
+        self.context.new_cast(None, val, ty)
+    }
+}
+
+pub trait SignType<'gcc, 'tcx> {
+    fn is_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_unsigned(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn to_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
+    fn to_unsigned(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
+}
+
+impl<'gcc, 'tcx> SignType<'gcc, 'tcx> for Type<'gcc> {
+    fn is_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.is_i8(cx) || self.is_i16(cx) || self.is_i32(cx) || self.is_i64(cx) || self.is_i128(cx)
+    }
+
+    fn is_unsigned(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.is_u8(cx) || self.is_u16(cx) || self.is_u32(cx) || self.is_u64(cx) || self.is_u128(cx)
+    }
+
+    fn to_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+        if self.is_u8(cx) {
+            cx.i8_type
+        }
+        else if self.is_u16(cx) {
+            cx.i16_type
+        }
+        else if self.is_u32(cx) {
+            cx.i32_type
+        }
+        else if self.is_u64(cx) {
+            cx.i64_type
+        }
+        else if self.is_u128(cx) {
+            cx.i128_type
+        }
+        else {
+            self.clone()
+        }
+    }
+
+    fn to_unsigned(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+        if self.is_i8(cx) {
+            cx.u8_type
+        }
+        else if self.is_i16(cx) {
+            cx.u16_type
+        }
+        else if self.is_i32(cx) {
+            cx.u32_type
+        }
+        else if self.is_i64(cx) {
+            cx.u64_type
+        }
+        else if self.is_i128(cx) {
+            cx.u128_type
+        }
+        else {
+            self.clone()
+        }
+    }
+}
+
+pub trait TypeReflection<'gcc, 'tcx>  {
+    fn is_uchar(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_ushort(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_uint(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_ulong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_ulonglong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+
+    fn is_i8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_u8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_i16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_u16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_i32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_u32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_i64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_u64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_i128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_u128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+
+    fn is_f32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+    fn is_f64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool;
+}
+
+impl<'gcc, 'tcx> TypeReflection<'gcc, 'tcx> for Type<'gcc> {
+    fn is_uchar(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.u8_type
+    }
+
+    fn is_ushort(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.u16_type
+    }
+
+    fn is_uint(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.uint_type
+    }
+
+    fn is_ulong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.ulong_type
+    }
+
+    fn is_ulonglong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.ulonglong_type
+    }
+
+    fn is_i8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.i8_type
+    }
+
+    fn is_u8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.u8_type
+    }
+
+    fn is_i16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.i16_type
+    }
+
+    fn is_u16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.u16_type
+    }
+
+    fn is_i32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.i32_type
+    }
+
+    fn is_u32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.u32_type
+    }
+
+    fn is_i64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.i64_type
+    }
+
+    fn is_u64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.u64_type
+    }
+
+    fn is_i128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.context.new_c_type(CType::Int128t)
+    }
+
+    fn is_u128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.context.new_c_type(CType::UInt128t)
+    }
+
+    fn is_f32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.context.new_type::<f32>()
+    }
+
+    fn is_f64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool {
+        self.unqualified() == cx.context.new_type::<f64>()
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs
new file mode 100644 (file)
index 0000000..205498a
--- /dev/null
@@ -0,0 +1,390 @@
+use gccjit::{LValue, RValue, ToRValue, Type};
+use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, DerivedTypeMethods, StaticMethods};
+use rustc_hir as hir;
+use rustc_hir::Node;
+use rustc_middle::{bug, span_bug};
+use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
+use rustc_middle::mir::mono::MonoItem;
+use rustc_middle::ty::{self, Instance, Ty};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::mir::interpret::{self, Allocation, ErrorHandled, Scalar as InterpScalar, read_target_uint};
+use rustc_span::Span;
+use rustc_span::def_id::DefId;
+use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange};
+
+use crate::base;
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+    pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> {
+        if value.get_type() == self.bool_type.make_pointer() {
+            if let Some(pointee) = typ.get_pointee() {
+                if pointee.is_vector().is_some() {
+                    panic!()
+                }
+            }
+        }
+        self.context.new_bitcast(None, value, typ)
+    }
+}
+
+impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> {
+    fn static_addr_of(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> {
+        if let Some(global_value) = self.const_globals.borrow().get(&cv) {
+            // TODO(antoyo): upgrade alignment.
+            return *global_value;
+        }
+        let global_value = self.static_addr_of_mut(cv, align, kind);
+        // TODO(antoyo): set global constant.
+        self.const_globals.borrow_mut().insert(cv, global_value);
+        global_value
+    }
+
+    fn codegen_static(&self, def_id: DefId, is_mutable: bool) {
+        let attrs = self.tcx.codegen_fn_attrs(def_id);
+
+        let value =
+            match codegen_static_initializer(&self, def_id) {
+                Ok((value, _)) => value,
+                // Error has already been reported
+                Err(_) => return,
+            };
+
+        let global = self.get_static(def_id);
+
+        // boolean SSA values are i1, but they have to be stored in i8 slots,
+        // otherwise some LLVM optimization passes don't work as expected
+        let val_llty = self.val_ty(value);
+        let value =
+            if val_llty == self.type_i1() {
+                unimplemented!();
+            }
+            else {
+                value
+            };
+
+        let instance = Instance::mono(self.tcx, def_id);
+        let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
+        let gcc_type = self.layout_of(ty).gcc_type(self, true);
+
+        // TODO(antoyo): set alignment.
+
+        let value =
+            if value.get_type() != gcc_type {
+                self.context.new_bitcast(None, value, gcc_type)
+            }
+            else {
+                value
+            };
+        global.global_set_initializer_value(value);
+
+        // As an optimization, all shared statics which do not have interior
+        // mutability are placed into read-only memory.
+        if !is_mutable {
+            if self.type_is_freeze(ty) {
+                // TODO(antoyo): set global constant.
+            }
+        }
+
+        if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
+            // Do not allow LLVM to change the alignment of a TLS on macOS.
+            //
+            // By default a global's alignment can be freely increased.
+            // This allows LLVM to generate more performant instructions
+            // e.g., using load-aligned into a SIMD register.
+            //
+            // However, on macOS 10.10 or below, the dynamic linker does not
+            // respect any alignment given on the TLS (radar 24221680).
+            // This will violate the alignment assumption, and causing segfault at runtime.
+            //
+            // This bug is very easy to trigger. In `println!` and `panic!`,
+            // the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS,
+            // which the values would be `mem::replace`d on initialization.
+            // The implementation of `mem::replace` will use SIMD
+            // whenever the size is 32 bytes or higher. LLVM notices SIMD is used
+            // and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary,
+            // which macOS's dyld disregarded and causing crashes
+            // (see issues #51794, #51758, #50867, #48866 and #44056).
+            //
+            // To workaround the bug, we trick LLVM into not increasing
+            // the global's alignment by explicitly assigning a section to it
+            // (equivalent to automatically generating a `#[link_section]` attribute).
+            // See the comment in the `GlobalValue::canIncreaseAlignment()` function
+            // of `lib/IR/Globals.cpp` for why this works.
+            //
+            // When the alignment is not increased, the optimized `mem::replace`
+            // will use load-unaligned instructions instead, and thus avoiding the crash.
+            //
+            // We could remove this hack whenever we decide to drop macOS 10.10 support.
+            if self.tcx.sess.target.options.is_like_osx {
+                // The `inspect` method is okay here because we checked relocations, and
+                // because we are doing this access to inspect the final interpreter state
+                // (not as part of the interpreter execution).
+                //
+                // FIXME: This check requires that the (arbitrary) value of undefined bytes
+                // happens to be zero. Instead, we should only check the value of defined bytes
+                // and set all undefined bytes to zero if this allocation is headed for the
+                // BSS.
+                unimplemented!();
+            }
+        }
+
+        // Wasm statics with custom link sections get special treatment as they
+        // go into custom sections of the wasm executable.
+        if self.tcx.sess.opts.target_triple.triple().starts_with("wasm32") {
+            if let Some(_section) = attrs.link_section {
+                unimplemented!();
+            }
+        } else {
+            // TODO(antoyo): set link section.
+        }
+
+        if attrs.flags.contains(CodegenFnAttrFlags::USED) {
+            self.add_used_global(global.to_rvalue());
+        }
+    }
+
+    /// Add a global value to a list to be stored in the `llvm.used` variable, an array of i8*.
+    fn add_used_global(&self, _global: RValue<'gcc>) {
+        // TODO(antoyo)
+    }
+
+    fn add_compiler_used_global(&self, _global: RValue<'gcc>) {
+        // TODO(antoyo)
+    }
+}
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+    pub fn static_addr_of_mut(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> {
+        let global =
+            match kind {
+                Some(kind) if !self.tcx.sess.fewer_names() => {
+                    let name = self.generate_local_symbol_name(kind);
+                    // TODO(antoyo): check if it's okay that TLS is off here.
+                    // TODO(antoyo): check if it's okay that link_section is None here.
+                    // TODO(antoyo): set alignment here as well.
+                    let global = self.define_global(&name[..], self.val_ty(cv), false, None);
+                    // TODO(antoyo): set linkage.
+                    global
+                }
+                _ => {
+                    let typ = self.val_ty(cv).get_aligned(align.bytes());
+                    let global = self.declare_unnamed_global(typ);
+                    global
+                },
+            };
+        // FIXME(antoyo): I think the name coming from generate_local_symbol_name() above cannot be used
+        // globally.
+        global.global_set_initializer_value(cv);
+        // TODO(antoyo): set unnamed address.
+        global.get_address(None)
+    }
+
+    pub fn get_static(&self, def_id: DefId) -> LValue<'gcc> {
+        let instance = Instance::mono(self.tcx, def_id);
+        let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
+        if let Some(&global) = self.instances.borrow().get(&instance) {
+            return global;
+        }
+
+        let defined_in_current_codegen_unit =
+            self.codegen_unit.items().contains_key(&MonoItem::Static(def_id));
+        assert!(
+            !defined_in_current_codegen_unit,
+            "consts::get_static() should always hit the cache for \
+                 statics defined in the same CGU, but did not for `{:?}`",
+            def_id
+        );
+
+        let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
+        let sym = self.tcx.symbol_name(instance).name;
+
+        let global =
+            if let Some(def_id) = def_id.as_local() {
+                let id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+                let llty = self.layout_of(ty).gcc_type(self, true);
+                // FIXME: refactor this to work without accessing the HIR
+                let global = match self.tcx.hir().get(id) {
+                    Node::Item(&hir::Item { span, kind: hir::ItemKind::Static(..), .. }) => {
+                        if let Some(global) = self.get_declared_value(&sym) {
+                            if self.val_ty(global) != self.type_ptr_to(llty) {
+                                span_bug!(span, "Conflicting types for static");
+                            }
+                        }
+
+                        let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
+                        let global = self.declare_global(&sym, llty, is_tls, fn_attrs.link_section);
+
+                        if !self.tcx.is_reachable_non_generic(def_id) {
+                            // TODO(antoyo): set visibility.
+                        }
+
+                        global
+                    }
+
+                    Node::ForeignItem(&hir::ForeignItem {
+                        span,
+                        kind: hir::ForeignItemKind::Static(..),
+                        ..
+                    }) => {
+                        let fn_attrs = self.tcx.codegen_fn_attrs(def_id);
+                        check_and_apply_linkage(&self, &fn_attrs, ty, sym, span)
+                    }
+
+                    item => bug!("get_static: expected static, found {:?}", item),
+                };
+
+                global
+            }
+            else {
+                // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow?
+                //debug!("get_static: sym={} item_attr={:?}", sym, self.tcx.item_attrs(def_id));
+
+                let attrs = self.tcx.codegen_fn_attrs(def_id);
+                let span = self.tcx.def_span(def_id);
+                let global = check_and_apply_linkage(&self, &attrs, ty, sym, span);
+
+                let needs_dll_storage_attr = false; // TODO(antoyo)
+
+                // If this assertion triggers, there's something wrong with commandline
+                // argument validation.
+                debug_assert!(
+                    !(self.tcx.sess.opts.cg.linker_plugin_lto.enabled()
+                        && self.tcx.sess.target.options.is_like_msvc
+                        && self.tcx.sess.opts.cg.prefer_dynamic)
+                );
+
+                if needs_dll_storage_attr {
+                    // This item is external but not foreign, i.e., it originates from an external Rust
+                    // crate. Since we don't know whether this crate will be linked dynamically or
+                    // statically in the final application, we always mark such symbols as 'dllimport'.
+                    // If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs
+                    // to make things work.
+                    //
+                    // However, in some scenarios we defer emission of statics to downstream
+                    // crates, so there are cases where a static with an upstream DefId
+                    // is actually present in the current crate. We can find out via the
+                    // is_codegened_item query.
+                    if !self.tcx.is_codegened_item(def_id) {
+                        unimplemented!();
+                    }
+                }
+                global
+            };
+
+        // TODO(antoyo): set dll storage class.
+
+        self.instances.borrow_mut().insert(instance, global);
+        global
+    }
+}
+
+pub fn const_alloc_to_gcc<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, alloc: &Allocation) -> RValue<'gcc> {
+    let mut llvals = Vec::with_capacity(alloc.relocations().len() + 1);
+    let dl = cx.data_layout();
+    let pointer_size = dl.pointer_size.bytes() as usize;
+
+    let mut next_offset = 0;
+    for &(offset, alloc_id) in alloc.relocations().iter() {
+        let offset = offset.bytes();
+        assert_eq!(offset as usize as u64, offset);
+        let offset = offset as usize;
+        if offset > next_offset {
+            // This `inspect` is okay since we have checked that it is not within a relocation, it
+            // is within the bounds of the allocation, and it doesn't affect interpreter execution
+            // (we inspect the result after interpreter execution). Any undef byte is replaced with
+            // some arbitrary byte value.
+            //
+            // FIXME: relay undef bytes to codegen as undef const bytes
+            let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(next_offset..offset);
+            llvals.push(cx.const_bytes(bytes));
+        }
+        let ptr_offset =
+            read_target_uint( dl.endian,
+                // This `inspect` is okay since it is within the bounds of the allocation, it doesn't
+                // affect interpreter execution (we inspect the result after interpreter execution),
+                // and we properly interpret the relocation as a relocation pointer offset.
+                alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)),
+            )
+            .expect("const_alloc_to_llvm: could not read relocation pointer")
+            as u64;
+        llvals.push(cx.scalar_to_backend(
+            InterpScalar::from_pointer(
+                interpret::Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
+                &cx.tcx,
+            ),
+            abi::Scalar { value: Primitive::Pointer, valid_range: WrappingRange { start: 0, end: !0 } },
+            cx.type_i8p(),
+        ));
+        next_offset = offset + pointer_size;
+    }
+    if alloc.len() >= next_offset {
+        let range = next_offset..alloc.len();
+        // This `inspect` is okay since we have check that it is after all relocations, it is
+        // within the bounds of the allocation, and it doesn't affect interpreter execution (we
+        // inspect the result after interpreter execution). Any undef byte is replaced with some
+        // arbitrary byte value.
+        //
+        // FIXME: relay undef bytes to codegen as undef const bytes
+        let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range);
+        llvals.push(cx.const_bytes(bytes));
+    }
+
+    cx.const_struct(&llvals, true)
+}
+
+pub fn codegen_static_initializer<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, def_id: DefId) -> Result<(RValue<'gcc>, &'tcx Allocation), ErrorHandled> {
+    let alloc = cx.tcx.eval_static_initializer(def_id)?;
+    Ok((const_alloc_to_gcc(cx, alloc), alloc))
+}
+
+fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &CodegenFnAttrs, ty: Ty<'tcx>, sym: &str, span: Span) -> LValue<'gcc> {
+    let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
+    let llty = cx.layout_of(ty).gcc_type(cx, true);
+    if let Some(linkage) = attrs.linkage {
+        // If this is a static with a linkage specified, then we need to handle
+        // it a little specially. The typesystem prevents things like &T and
+        // extern "C" fn() from being non-null, so we can't just declare a
+        // static and call it a day. Some linkages (like weak) will make it such
+        // that the static actually has a null value.
+        let llty2 =
+            if let ty::RawPtr(ref mt) = ty.kind() {
+                cx.layout_of(mt.ty).gcc_type(cx, true)
+            }
+            else {
+                cx.sess().span_fatal(
+                    span,
+                    "must have type `*const T` or `*mut T` due to `#[linkage]` attribute",
+                )
+            };
+        // Declare a symbol `foo` with the desired linkage.
+        let global1 = cx.declare_global_with_linkage(&sym, llty2, base::global_linkage_to_gcc(linkage));
+
+        // Declare an internal global `extern_with_linkage_foo` which
+        // is initialized with the address of `foo`.  If `foo` is
+        // discarded during linking (for example, if `foo` has weak
+        // linkage and there are no definitions), then
+        // `extern_with_linkage_foo` will instead be initialized to
+        // zero.
+        let mut real_name = "_rust_extern_with_linkage_".to_string();
+        real_name.push_str(&sym);
+        let global2 = cx.define_global(&real_name, llty, is_tls, attrs.link_section);
+        // TODO(antoyo): set linkage.
+        global2.global_set_initializer_value(global1.get_address(None));
+        // TODO(antoyo): use global_set_initializer() when it will work.
+        global2
+    }
+    else {
+        // Generate an external declaration.
+        // FIXME(nagisa): investigate whether it can be changed into define_global
+
+        // Thread-local statics in some other crate need to *always* be linked
+        // against in a thread-local fashion, so we need to be sure to apply the
+        // thread-local attribute locally if it was present remotely. If we
+        // don't do this then linker errors can be generated where the linker
+        // complains that one object files has a thread local version of the
+        // symbol and another one doesn't.
+        cx.declare_global(&sym, llty, is_tls, attrs.link_section)
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs
new file mode 100644 (file)
index 0000000..7677ade
--- /dev/null
@@ -0,0 +1,475 @@
+use std::cell::{Cell, RefCell};
+
+use gccjit::{
+    Block,
+    Context,
+    CType,
+    Function,
+    FunctionType,
+    LValue,
+    RValue,
+    Struct,
+    Type,
+};
+use rustc_codegen_ssa::base::wants_msvc_seh;
+use rustc_codegen_ssa::traits::{
+    BackendTypes,
+    MiscMethods,
+};
+use rustc_data_structures::base_n;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_middle::span_bug;
+use rustc_middle::mir::mono::CodegenUnit;
+use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt};
+use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, TyAndLayout, LayoutOfHelpers};
+use rustc_session::Session;
+use rustc_span::{Span, Symbol};
+use rustc_target::abi::{call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
+use rustc_target::spec::{HasTargetSpec, Target, TlsModel};
+
+use crate::callee::get_fn;
+use crate::declare::mangle_name;
+
+#[derive(Clone)]
+pub struct FuncSig<'gcc> {
+    pub params: Vec<Type<'gcc>>,
+    pub return_type: Type<'gcc>,
+}
+
+pub struct CodegenCx<'gcc, 'tcx> {
+    pub check_overflow: bool,
+    pub codegen_unit: &'tcx CodegenUnit<'tcx>,
+    pub context: &'gcc Context<'gcc>,
+
+    // TODO(antoyo): First set it to a dummy block to avoid using Option?
+    pub current_block: RefCell<Option<Block<'gcc>>>,
+    pub current_func: RefCell<Option<Function<'gcc>>>,
+    pub normal_function_addresses: RefCell<FxHashSet<RValue<'gcc>>>,
+
+    pub functions: RefCell<FxHashMap<String, Function<'gcc>>>,
+
+    pub tls_model: gccjit::TlsModel,
+
+    pub bool_type: Type<'gcc>,
+    pub i8_type: Type<'gcc>,
+    pub i16_type: Type<'gcc>,
+    pub i32_type: Type<'gcc>,
+    pub i64_type: Type<'gcc>,
+    pub i128_type: Type<'gcc>,
+    pub isize_type: Type<'gcc>,
+
+    pub u8_type: Type<'gcc>,
+    pub u16_type: Type<'gcc>,
+    pub u32_type: Type<'gcc>,
+    pub u64_type: Type<'gcc>,
+    pub u128_type: Type<'gcc>,
+    pub usize_type: Type<'gcc>,
+
+    pub int_type: Type<'gcc>,
+    pub uint_type: Type<'gcc>,
+    pub long_type: Type<'gcc>,
+    pub ulong_type: Type<'gcc>,
+    pub ulonglong_type: Type<'gcc>,
+    pub sizet_type: Type<'gcc>,
+
+    pub float_type: Type<'gcc>,
+    pub double_type: Type<'gcc>,
+
+    pub linkage: Cell<FunctionType>,
+    pub scalar_types: RefCell<FxHashMap<Ty<'tcx>, Type<'gcc>>>,
+    pub types: RefCell<FxHashMap<(Ty<'tcx>, Option<VariantIdx>), Type<'gcc>>>,
+    pub tcx: TyCtxt<'tcx>,
+
+    pub struct_types: RefCell<FxHashMap<Vec<Type<'gcc>>, Type<'gcc>>>,
+
+    pub types_with_fields_to_set: RefCell<FxHashMap<Type<'gcc>, (Struct<'gcc>, TyAndLayout<'tcx>)>>,
+
+    /// Cache instances of monomorphic and polymorphic items
+    pub instances: RefCell<FxHashMap<Instance<'tcx>, LValue<'gcc>>>,
+    /// Cache function instances of monomorphic and polymorphic items
+    pub function_instances: RefCell<FxHashMap<Instance<'tcx>, RValue<'gcc>>>,
+    /// Cache generated vtables
+    pub vtables: RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>>,
+
+    /// Cache of emitted const globals (value -> global)
+    pub const_globals: RefCell<FxHashMap<RValue<'gcc>, RValue<'gcc>>>,
+
+    /// Cache of constant strings,
+    pub const_cstr_cache: RefCell<FxHashMap<Symbol, LValue<'gcc>>>,
+
+    /// Cache of globals.
+    pub globals: RefCell<FxHashMap<String, RValue<'gcc>>>,
+
+    /// A counter that is used for generating local symbol names
+    local_gen_sym_counter: Cell<usize>,
+    pub global_gen_sym_counter: Cell<usize>,
+
+    eh_personality: Cell<Option<RValue<'gcc>>>,
+
+    pub pointee_infos: RefCell<FxHashMap<(Ty<'tcx>, Size), Option<PointeeInfo>>>,
+
+    /// NOTE: a hack is used because the rustc API is not suitable to libgccjit and as such,
+    /// `const_undef()` returns struct as pointer so that they can later be assigned a value.
+    /// As such, this set remembers which of these pointers were returned by this function so that
+    /// they can be deferenced later.
+    /// FIXME(antoyo): fix the rustc API to avoid having this hack.
+    pub structs_as_pointer: RefCell<FxHashSet<RValue<'gcc>>>,
+}
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+    pub fn new(context: &'gcc Context<'gcc>, codegen_unit: &'tcx CodegenUnit<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
+        let check_overflow = tcx.sess.overflow_checks();
+        // TODO(antoyo): fix this mess. libgccjit seems to return random type when using new_int_type().
+        let isize_type = context.new_c_type(CType::LongLong);
+        let usize_type = context.new_c_type(CType::ULongLong);
+        let bool_type = context.new_type::<bool>();
+        let i8_type = context.new_type::<i8>();
+        let i16_type = context.new_type::<i16>();
+        let i32_type = context.new_type::<i32>();
+        let i64_type = context.new_c_type(CType::LongLong);
+        let i128_type = context.new_c_type(CType::Int128t).get_aligned(8); // TODO(antoyo): should the alignment be hard-coded?
+        let u8_type = context.new_type::<u8>();
+        let u16_type = context.new_type::<u16>();
+        let u32_type = context.new_type::<u32>();
+        let u64_type = context.new_c_type(CType::ULongLong);
+        let u128_type = context.new_c_type(CType::UInt128t).get_aligned(8); // TODO(antoyo): should the alignment be hard-coded?
+
+        let tls_model = to_gcc_tls_mode(tcx.sess.tls_model());
+
+        let float_type = context.new_type::<f32>();
+        let double_type = context.new_type::<f64>();
+
+        let int_type = context.new_c_type(CType::Int);
+        let uint_type = context.new_c_type(CType::UInt);
+        let long_type = context.new_c_type(CType::Long);
+        let ulong_type = context.new_c_type(CType::ULong);
+        let ulonglong_type = context.new_c_type(CType::ULongLong);
+        let sizet_type = context.new_c_type(CType::SizeT);
+
+        assert_eq!(isize_type, i64_type);
+        assert_eq!(usize_type, u64_type);
+
+        let mut functions = FxHashMap::default();
+        let builtins = [
+            "__builtin_unreachable", "abort", "__builtin_expect", "__builtin_add_overflow", "__builtin_mul_overflow",
+            "__builtin_saddll_overflow", /*"__builtin_sadd_overflow",*/ "__builtin_smulll_overflow", /*"__builtin_smul_overflow",*/
+            "__builtin_ssubll_overflow", /*"__builtin_ssub_overflow",*/ "__builtin_sub_overflow", "__builtin_uaddll_overflow",
+            "__builtin_uadd_overflow", "__builtin_umulll_overflow", "__builtin_umul_overflow", "__builtin_usubll_overflow",
+            "__builtin_usub_overflow", "sqrtf", "sqrt", "__builtin_powif", "__builtin_powi", "sinf", "sin", "cosf", "cos",
+            "powf", "pow", "expf", "exp", "exp2f", "exp2", "logf", "log", "log10f", "log10", "log2f", "log2", "fmaf",
+            "fma", "fabsf", "fabs", "fminf", "fmin", "fmaxf", "fmax", "copysignf", "copysign", "floorf", "floor", "ceilf",
+            "ceil", "truncf", "trunc", "rintf", "rint", "nearbyintf", "nearbyint", "roundf", "round",
+            "__builtin_expect_with_probability",
+        ];
+
+        for builtin in builtins.iter() {
+            functions.insert(builtin.to_string(), context.get_builtin_function(builtin));
+        }
+
+        Self {
+            check_overflow,
+            codegen_unit,
+            context,
+            current_block: RefCell::new(None),
+            current_func: RefCell::new(None),
+            normal_function_addresses: Default::default(),
+            functions: RefCell::new(functions),
+
+            tls_model,
+
+            bool_type,
+            i8_type,
+            i16_type,
+            i32_type,
+            i64_type,
+            i128_type,
+            isize_type,
+            usize_type,
+            u8_type,
+            u16_type,
+            u32_type,
+            u64_type,
+            u128_type,
+            int_type,
+            uint_type,
+            long_type,
+            ulong_type,
+            ulonglong_type,
+            sizet_type,
+
+            float_type,
+            double_type,
+
+            linkage: Cell::new(FunctionType::Internal),
+            instances: Default::default(),
+            function_instances: Default::default(),
+            vtables: Default::default(),
+            const_globals: Default::default(),
+            const_cstr_cache: Default::default(),
+            globals: Default::default(),
+            scalar_types: Default::default(),
+            types: Default::default(),
+            tcx,
+            struct_types: Default::default(),
+            types_with_fields_to_set: Default::default(),
+            local_gen_sym_counter: Cell::new(0),
+            global_gen_sym_counter: Cell::new(0),
+            eh_personality: Cell::new(None),
+            pointee_infos: Default::default(),
+            structs_as_pointer: Default::default(),
+        }
+    }
+
+    pub fn rvalue_as_function(&self, value: RValue<'gcc>) -> Function<'gcc> {
+        let function: Function<'gcc> = unsafe { std::mem::transmute(value) };
+        debug_assert!(self.functions.borrow().values().find(|value| **value == function).is_some(),
+            "{:?} ({:?}) is not a function", value, value.get_type());
+        function
+    }
+
+    pub fn sess(&self) -> &Session {
+        &self.tcx.sess
+    }
+}
+
+impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> {
+    type Value = RValue<'gcc>;
+    type Function = RValue<'gcc>;
+
+    type BasicBlock = Block<'gcc>;
+    type Type = Type<'gcc>;
+    type Funclet = (); // TODO(antoyo)
+
+    type DIScope = (); // TODO(antoyo)
+    type DILocation = (); // TODO(antoyo)
+    type DIVariable = (); // TODO(antoyo)
+}
+
+impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn vtables(&self) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>> {
+        &self.vtables
+    }
+
+    fn get_fn(&self, instance: Instance<'tcx>) -> RValue<'gcc> {
+        let func = get_fn(self, instance);
+        *self.current_func.borrow_mut() = Some(self.rvalue_as_function(func));
+        func
+    }
+
+    fn get_fn_addr(&self, instance: Instance<'tcx>) -> RValue<'gcc> {
+        let func = get_fn(self, instance);
+        let func = self.rvalue_as_function(func);
+        let ptr = func.get_address(None);
+
+        // TODO(antoyo): don't do this twice: i.e. in declare_fn and here.
+        // FIXME(antoyo): the rustc API seems to call get_fn_addr() when not needed (e.g. for FFI).
+
+        self.normal_function_addresses.borrow_mut().insert(ptr);
+
+        ptr
+    }
+
+    fn eh_personality(&self) -> RValue<'gcc> {
+        // The exception handling personality function.
+        //
+        // If our compilation unit has the `eh_personality` lang item somewhere
+        // within it, then we just need to codegen that. Otherwise, we're
+        // building an rlib which will depend on some upstream implementation of
+        // this function, so we just codegen a generic reference to it. We don't
+        // specify any of the types for the function, we just make it a symbol
+        // that LLVM can later use.
+        //
+        // Note that MSVC is a little special here in that we don't use the
+        // `eh_personality` lang item at all. Currently LLVM has support for
+        // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the
+        // *name of the personality function* to decide what kind of unwind side
+        // tables/landing pads to emit. It looks like Dwarf is used by default,
+        // injecting a dependency on the `_Unwind_Resume` symbol for resuming
+        // an "exception", but for MSVC we want to force SEH. This means that we
+        // can't actually have the personality function be our standard
+        // `rust_eh_personality` function, but rather we wired it up to the
+        // CRT's custom personality function, which forces LLVM to consider
+        // landing pads as "landing pads for SEH".
+        if let Some(llpersonality) = self.eh_personality.get() {
+            return llpersonality;
+        }
+        let tcx = self.tcx;
+        let llfn = match tcx.lang_items().eh_personality() {
+            Some(def_id) if !wants_msvc_seh(self.sess()) => self.get_fn_addr(
+                ty::Instance::resolve(
+                    tcx,
+                    ty::ParamEnv::reveal_all(),
+                    def_id,
+                    tcx.intern_substs(&[]),
+                )
+                .unwrap().unwrap(),
+            ),
+            _ => {
+                let _name = if wants_msvc_seh(self.sess()) {
+                    "__CxxFrameHandler3"
+                } else {
+                    "rust_eh_personality"
+                };
+                //let func = self.declare_func(name, self.type_i32(), &[], true);
+                // FIXME(antoyo): this hack should not be needed. That will probably be removed when
+                // unwinding support is added.
+                self.context.new_rvalue_from_int(self.int_type, 0)
+            }
+        };
+        // TODO(antoyo): apply target cpu attributes.
+        self.eh_personality.set(Some(llfn));
+        llfn
+    }
+
+    fn sess(&self) -> &Session {
+        &self.tcx.sess
+    }
+
+    fn check_overflow(&self) -> bool {
+        self.check_overflow
+    }
+
+    fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> {
+        self.codegen_unit
+    }
+
+    fn used_statics(&self) -> &RefCell<Vec<RValue<'gcc>>> {
+        unimplemented!();
+    }
+
+    fn set_frame_pointer_type(&self, _llfn: RValue<'gcc>) {
+        // TODO(antoyo)
+    }
+
+    fn apply_target_cpu_attr(&self, _llfn: RValue<'gcc>) {
+        // TODO(antoyo)
+    }
+
+    fn create_used_variable(&self) {
+        unimplemented!();
+    }
+
+    fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function> {
+        if self.get_declared_value("main").is_none() {
+            Some(self.declare_cfn("main", fn_type))
+        }
+        else {
+            // If the symbol already exists, it is an error: for example, the user wrote
+            // #[no_mangle] extern "C" fn main(..) {..}
+            // instead of #[start]
+            None
+        }
+    }
+
+    fn compiler_used_statics(&self) -> &RefCell<Vec<RValue<'gcc>>> {
+        unimplemented!()
+    }
+
+    fn create_compiler_used_variable(&self) {
+        unimplemented!()
+    }
+}
+
+impl<'gcc, 'tcx> HasTyCtxt<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+}
+
+impl<'gcc, 'tcx> HasDataLayout for CodegenCx<'gcc, 'tcx> {
+    fn data_layout(&self) -> &TargetDataLayout {
+        &self.tcx.data_layout
+    }
+}
+
+impl<'gcc, 'tcx> HasTargetSpec for CodegenCx<'gcc, 'tcx> {
+    fn target_spec(&self) -> &Target {
+        &self.tcx.sess.target
+    }
+}
+
+impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
+    type LayoutOfResult = TyAndLayout<'tcx>;
+
+    #[inline]
+    fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
+        if let LayoutError::SizeOverflow(_) = err {
+            self.sess().span_fatal(span, &err.to_string())
+        } else {
+            span_bug!(span, "failed to get layout for `{}`: {}", ty, err)
+        }
+    }
+}
+
+impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
+    type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>;
+
+    #[inline]
+    fn handle_fn_abi_err(
+        &self,
+        err: FnAbiError<'tcx>,
+        span: Span,
+        fn_abi_request: FnAbiRequest<'tcx>,
+    ) -> ! {
+        if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
+            self.sess().span_fatal(span, &err.to_string())
+        } else {
+            match fn_abi_request {
+                FnAbiRequest::OfFnPtr { sig, extra_args } => {
+                    span_bug!(
+                        span,
+                        "`fn_abi_of_fn_ptr({}, {:?})` failed: {}",
+                        sig,
+                        extra_args,
+                        err
+                    );
+                }
+                FnAbiRequest::OfInstance { instance, extra_args } => {
+                    span_bug!(
+                        span,
+                        "`fn_abi_of_instance({}, {:?})` failed: {}",
+                        instance,
+                        extra_args,
+                        err
+                    );
+                }
+            }
+        }
+    }
+}
+
+impl<'tcx, 'gcc> HasParamEnv<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn param_env(&self) -> ParamEnv<'tcx> {
+        ParamEnv::reveal_all()
+    }
+}
+
+impl<'b, 'tcx> CodegenCx<'b, 'tcx> {
+    /// Generates a new symbol name with the given prefix. This symbol name must
+    /// only be used for definitions with `internal` or `private` linkage.
+    pub fn generate_local_symbol_name(&self, prefix: &str) -> String {
+        let idx = self.local_gen_sym_counter.get();
+        self.local_gen_sym_counter.set(idx + 1);
+        // Include a '.' character, so there can be no accidental conflicts with
+        // user defined names
+        let mut name = String::with_capacity(prefix.len() + 6);
+        name.push_str(prefix);
+        name.push_str(".");
+        base_n::push_str(idx as u128, base_n::ALPHANUMERIC_ONLY, &mut name);
+        name
+    }
+}
+
+pub fn unit_name<'tcx>(codegen_unit: &CodegenUnit<'tcx>) -> String {
+    let name = &codegen_unit.name().to_string();
+    mangle_name(&name.replace('-', "_"))
+}
+
+fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel {
+    match tls_model {
+        TlsModel::GeneralDynamic => gccjit::TlsModel::GlobalDynamic,
+        TlsModel::LocalDynamic => gccjit::TlsModel::LocalDynamic,
+        TlsModel::InitialExec => gccjit::TlsModel::InitialExec,
+        TlsModel::LocalExec => gccjit::TlsModel::LocalExec,
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/coverageinfo.rs b/compiler/rustc_codegen_gcc/src/coverageinfo.rs
new file mode 100644 (file)
index 0000000..872fc24
--- /dev/null
@@ -0,0 +1,69 @@
+use gccjit::RValue;
+use rustc_codegen_ssa::traits::{CoverageInfoBuilderMethods, CoverageInfoMethods};
+use rustc_hir::def_id::DefId;
+use rustc_middle::mir::coverage::{
+    CodeRegion,
+    CounterValueReference,
+    ExpressionOperandId,
+    InjectedExpressionId,
+    Op,
+};
+use rustc_middle::ty::Instance;
+
+use crate::builder::Builder;
+use crate::context::CodegenCx;
+
+impl<'a, 'gcc, 'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
+    fn set_function_source_hash(
+        &mut self,
+        _instance: Instance<'tcx>,
+        _function_source_hash: u64,
+    ) -> bool {
+        unimplemented!();
+    }
+
+    fn add_coverage_counter(&mut self, _instance: Instance<'tcx>, _id: CounterValueReference, _region: CodeRegion) -> bool {
+        // TODO(antoyo)
+        false
+    }
+
+    fn add_coverage_counter_expression(&mut self, _instance: Instance<'tcx>, _id: InjectedExpressionId, _lhs: ExpressionOperandId, _op: Op, _rhs: ExpressionOperandId, _region: Option<CodeRegion>) -> bool {
+        // TODO(antoyo)
+        false
+    }
+
+    fn add_coverage_unreachable(&mut self, _instance: Instance<'tcx>, _region: CodeRegion) -> bool {
+        // TODO(antoyo)
+        false
+    }
+}
+
+impl<'gcc, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn coverageinfo_finalize(&self) {
+        // TODO(antoyo)
+    }
+
+    fn get_pgo_func_name_var(&self, _instance: Instance<'tcx>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    /// Functions with MIR-based coverage are normally codegenned _only_ if
+    /// called. LLVM coverage tools typically expect every function to be
+    /// defined (even if unused), with at least one call to LLVM intrinsic
+    /// `instrprof.increment`.
+    ///
+    /// Codegen a small function that will never be called, with one counter
+    /// that will never be incremented.
+    ///
+    /// For used/called functions, the coverageinfo was already added to the
+    /// `function_coverage_map` (keyed by function `Instance`) during codegen.
+    /// But in this case, since the unused function was _not_ previously
+    /// codegenned, collect the coverage `CodeRegion`s from the MIR and add
+    /// them. The first `CodeRegion` is used to add a single counter, with the
+    /// same counter ID used in the injected `instrprof.increment` intrinsic
+    /// call. Since the function is never called, all other `CodeRegion`s can be
+    /// added as `unreachable_region`s.
+    fn define_unused_fn(&self, _def_id: DefId) {
+        unimplemented!();
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs
new file mode 100644 (file)
index 0000000..4d3b4f0
--- /dev/null
@@ -0,0 +1,62 @@
+use gccjit::RValue;
+use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, VariableKind};
+use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoMethods};
+use rustc_middle::mir;
+use rustc_middle::ty::{Instance, Ty};
+use rustc_span::{SourceFile, Span, Symbol};
+use rustc_target::abi::Size;
+use rustc_target::abi::call::FnAbi;
+
+use crate::builder::Builder;
+use crate::context::CodegenCx;
+
+impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
+    // FIXME(eddyb) find a common convention for all of the debuginfo-related
+    // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
+    fn dbg_var_addr(&mut self, _dbg_var: Self::DIVariable, _scope_metadata: Self::DIScope, _variable_alloca: Self::Value, _direct_offset: Size, _indirect_offsets: &[Size]) {
+        unimplemented!();
+    }
+
+    fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {
+        // TODO(antoyo): insert reference to gdb debug scripts section global.
+    }
+
+    fn set_var_name(&mut self, _value: RValue<'gcc>, _name: &str) {
+        unimplemented!();
+    }
+
+    fn set_dbg_loc(&mut self, _dbg_loc: Self::DILocation) {
+        unimplemented!();
+    }
+}
+
+impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn create_vtable_metadata(&self, _ty: Ty<'tcx>, _vtable: Self::Value) {
+        // TODO(antoyo)
+    }
+
+    fn create_function_debug_context(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _llfn: RValue<'gcc>, _mir: &mir::Body<'tcx>) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
+        // TODO(antoyo)
+        None
+    }
+
+    fn extend_scope_to_file(&self, _scope_metadata: Self::DIScope, _file: &SourceFile) -> Self::DIScope {
+        unimplemented!();
+    }
+
+    fn debuginfo_finalize(&self) {
+        // TODO(antoyo)
+    }
+
+    fn create_dbg_var(&self, _variable_name: Symbol, _variable_type: Ty<'tcx>, _scope_metadata: Self::DIScope, _variable_kind: VariableKind, _span: Span) -> Self::DIVariable {
+        unimplemented!();
+    }
+
+    fn dbg_scope_fn(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _maybe_definition_llfn: Option<RValue<'gcc>>) -> Self::DIScope {
+        unimplemented!();
+    }
+
+    fn dbg_loc(&self, _scope: Self::DIScope, _inlined_at: Option<Self::DILocation>, _span: Span) -> Self::DILocation {
+        unimplemented!();
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs
new file mode 100644 (file)
index 0000000..b79a50d
--- /dev/null
@@ -0,0 +1,144 @@
+use gccjit::{Function, FunctionType, GlobalKind, LValue, RValue, Type};
+use rustc_codegen_ssa::traits::BaseTypeMethods;
+use rustc_middle::ty::Ty;
+use rustc_span::Symbol;
+use rustc_target::abi::call::FnAbi;
+
+use crate::abi::FnAbiGccExt;
+use crate::context::{CodegenCx, unit_name};
+use crate::intrinsic::llvm;
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+    pub fn get_or_insert_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option<Symbol>) -> LValue<'gcc> {
+        if self.globals.borrow().contains_key(name) {
+            let typ = self.globals.borrow().get(name).expect("global").get_type();
+            let global = self.context.new_global(None, GlobalKind::Imported, typ, name);
+            if is_tls {
+                global.set_tls_model(self.tls_model);
+            }
+            if let Some(link_section) = link_section {
+                global.set_link_section(&link_section.as_str());
+            }
+            global
+        }
+        else {
+            self.declare_global(name, ty, is_tls, link_section)
+        }
+    }
+
+    pub fn declare_unnamed_global(&self, ty: Type<'gcc>) -> LValue<'gcc> {
+        let index = self.global_gen_sym_counter.get();
+        self.global_gen_sym_counter.set(index + 1);
+        let name = format!("global_{}_{}", index, unit_name(&self.codegen_unit));
+        self.context.new_global(None, GlobalKind::Exported, ty, &name)
+    }
+
+    pub fn declare_global_with_linkage(&self, name: &str, ty: Type<'gcc>, linkage: GlobalKind) -> LValue<'gcc> {
+        let global = self.context.new_global(None, linkage, ty, name);
+        let global_address = global.get_address(None);
+        self.globals.borrow_mut().insert(name.to_string(), global_address);
+        global
+    }
+
+    /*pub fn declare_func(&self, name: &str, return_type: Type<'gcc>, params: &[Type<'gcc>], variadic: bool) -> RValue<'gcc> {
+        self.linkage.set(FunctionType::Exported);
+        let func = declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, params, variadic);
+        // FIXME(antoyo): this is a wrong cast. That requires changing the compiler API.
+        unsafe { std::mem::transmute(func) }
+    }*/
+
+    pub fn declare_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option<Symbol>) -> LValue<'gcc> {
+        let global = self.context.new_global(None, GlobalKind::Exported, ty, name);
+        if is_tls {
+            global.set_tls_model(self.tls_model);
+        }
+        if let Some(link_section) = link_section {
+            global.set_link_section(&link_section.as_str());
+        }
+        let global_address = global.get_address(None);
+        self.globals.borrow_mut().insert(name.to_string(), global_address);
+        global
+    }
+
+    pub fn declare_private_global(&self, name: &str, ty: Type<'gcc>) -> LValue<'gcc> {
+        let global = self.context.new_global(None, GlobalKind::Internal, ty, name);
+        let global_address = global.get_address(None);
+        self.globals.borrow_mut().insert(name.to_string(), global_address);
+        global
+    }
+
+    pub fn declare_cfn(&self, name: &str, _fn_type: Type<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): use the fn_type parameter.
+        let const_string = self.context.new_type::<u8>().make_pointer().make_pointer();
+        let return_type = self.type_i32();
+        let variadic = false;
+        self.linkage.set(FunctionType::Exported);
+        let func = declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, &[self.type_i32(), const_string], variadic);
+        // NOTE: it is needed to set the current_func here as well, because get_fn() is not called
+        // for the main function.
+        *self.current_func.borrow_mut() = Some(func);
+        // FIXME(antoyo): this is a wrong cast. That requires changing the compiler API.
+        unsafe { std::mem::transmute(func) }
+    }
+
+    pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> RValue<'gcc> {
+        let (return_type, params, variadic) = fn_abi.gcc_type(self);
+        let func = declare_raw_fn(self, name, () /*fn_abi.llvm_cconv()*/, return_type, &params, variadic);
+        // FIXME(antoyo): this is a wrong cast. That requires changing the compiler API.
+        unsafe { std::mem::transmute(func) }
+    }
+
+    pub fn define_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option<Symbol>) -> LValue<'gcc> {
+        self.get_or_insert_global(name, ty, is_tls, link_section)
+    }
+
+    pub fn get_declared_value(&self, name: &str) -> Option<RValue<'gcc>> {
+        // TODO(antoyo): use a different field than globals, because this seems to return a function?
+        self.globals.borrow().get(name).cloned()
+    }
+}
+
+/// Declare a function.
+///
+/// If there’s a value with the same name already declared, the function will
+/// update the declaration and return existing Value instead.
+fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, _callconv: () /*llvm::CallConv*/, return_type: Type<'gcc>, param_types: &[Type<'gcc>], variadic: bool) -> Function<'gcc> {
+    if name.starts_with("llvm.") {
+        return llvm::intrinsic(name, cx);
+    }
+    let func =
+        if cx.functions.borrow().contains_key(name) {
+            *cx.functions.borrow().get(name).expect("function")
+        }
+        else {
+            let params: Vec<_> = param_types.into_iter().enumerate()
+                .map(|(index, param)| cx.context.new_parameter(None, *param, &format!("param{}", index))) // TODO(antoyo): set name.
+                .collect();
+            let func = cx.context.new_function(None, cx.linkage.get(), return_type, &params, mangle_name(name), variadic);
+            cx.functions.borrow_mut().insert(name.to_string(), func);
+            func
+        };
+
+    // TODO(antoyo): set function calling convention.
+    // TODO(antoyo): set unnamed address.
+    // TODO(antoyo): set no red zone function attribute.
+    // TODO(antoyo): set attributes for optimisation.
+    // TODO(antoyo): set attributes for non lazy bind.
+
+    // FIXME(antoyo): invalid cast.
+    func
+}
+
+// FIXME(antoyo): this is a hack because libgccjit currently only supports alpha, num and _.
+// Unsupported characters: `$` and `.`.
+pub fn mangle_name(name: &str) -> String {
+    name.replace(|char: char| {
+        if !char.is_alphanumeric() && char != '_' {
+            debug_assert!("$.".contains(char), "Unsupported char in function name: {}", char);
+            true
+        }
+        else {
+            false
+        }
+    }, "_")
+}
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs
new file mode 100644 (file)
index 0000000..b074feb
--- /dev/null
@@ -0,0 +1,22 @@
+use gccjit::Function;
+
+use crate::context::CodegenCx;
+
+pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function<'gcc> {
+    let _gcc_name =
+        match name {
+            "llvm.x86.xgetbv" => {
+                let gcc_name = "__builtin_trap";
+                let func = cx.context.get_builtin_function(gcc_name);
+                cx.functions.borrow_mut().insert(gcc_name.to_string(), func);
+                return func;
+            },
+            // NOTE: this doc specifies the equivalent GCC builtins: http://huonw.github.io/llvmint/llvmint/x86/index.html
+            "llvm.x86.sse2.cmp.pd" => "__builtin_ia32_cmppd",
+            "llvm.x86.sse2.movmsk.pd" => "__builtin_ia32_movmskpd",
+            "llvm.x86.sse2.pmovmskb.128" => "__builtin_ia32_pmovmskb128",
+            _ => unimplemented!("unsupported LLVM intrinsic {}", name)
+        };
+
+    unimplemented!();
+}
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
new file mode 100644 (file)
index 0000000..375d422
--- /dev/null
@@ -0,0 +1,1067 @@
+pub mod llvm;
+mod simd;
+
+use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp};
+use rustc_codegen_ssa::MemFlags;
+use rustc_codegen_ssa::base::wants_msvc_seh;
+use rustc_codegen_ssa::common::{IntPredicate, span_invalid_monomorphization_error};
+use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
+use rustc_codegen_ssa::mir::place::PlaceRef;
+use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods};
+use rustc_middle::bug;
+use rustc_middle::ty::{self, Instance, Ty};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_span::{Span, Symbol, symbol::kw, sym};
+use rustc_target::abi::HasDataLayout;
+use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
+use rustc_target::spec::PanicStrategy;
+
+use crate::abi::GccType;
+use crate::builder::Builder;
+use crate::common::{SignType, TypeReflection};
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+use crate::intrinsic::simd::generic_simd_intrinsic;
+
+fn get_simple_intrinsic<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, name: Symbol) -> Option<Function<'gcc>> {
+    let gcc_name = match name {
+        sym::sqrtf32 => "sqrtf",
+        sym::sqrtf64 => "sqrt",
+        sym::powif32 => "__builtin_powif",
+        sym::powif64 => "__builtin_powi",
+        sym::sinf32 => "sinf",
+        sym::sinf64 => "sin",
+        sym::cosf32 => "cosf",
+        sym::cosf64 => "cos",
+        sym::powf32 => "powf",
+        sym::powf64 => "pow",
+        sym::expf32 => "expf",
+        sym::expf64 => "exp",
+        sym::exp2f32 => "exp2f",
+        sym::exp2f64 => "exp2",
+        sym::logf32 => "logf",
+        sym::logf64 => "log",
+        sym::log10f32 => "log10f",
+        sym::log10f64 => "log10",
+        sym::log2f32 => "log2f",
+        sym::log2f64 => "log2",
+        sym::fmaf32 => "fmaf",
+        sym::fmaf64 => "fma",
+        sym::fabsf32 => "fabsf",
+        sym::fabsf64 => "fabs",
+        sym::minnumf32 => "fminf",
+        sym::minnumf64 => "fmin",
+        sym::maxnumf32 => "fmaxf",
+        sym::maxnumf64 => "fmax",
+        sym::copysignf32 => "copysignf",
+        sym::copysignf64 => "copysign",
+        sym::floorf32 => "floorf",
+        sym::floorf64 => "floor",
+        sym::ceilf32 => "ceilf",
+        sym::ceilf64 => "ceil",
+        sym::truncf32 => "truncf",
+        sym::truncf64 => "trunc",
+        sym::rintf32 => "rintf",
+        sym::rintf64 => "rint",
+        sym::nearbyintf32 => "nearbyintf",
+        sym::nearbyintf64 => "nearbyint",
+        sym::roundf32 => "roundf",
+        sym::roundf64 => "round",
+        sym::abort => "abort",
+        _ => return None,
+    };
+    Some(cx.context.get_builtin_function(&gcc_name))
+}
+
+impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
+    fn codegen_intrinsic_call(&mut self, instance: Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, RValue<'gcc>>], llresult: RValue<'gcc>, span: Span) {
+        let tcx = self.tcx;
+        let callee_ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
+
+        let (def_id, substs) = match *callee_ty.kind() {
+            ty::FnDef(def_id, substs) => (def_id, substs),
+            _ => bug!("expected fn item type, found {}", callee_ty),
+        };
+
+        let sig = callee_ty.fn_sig(tcx);
+        let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig);
+        let arg_tys = sig.inputs();
+        let ret_ty = sig.output();
+        let name = tcx.item_name(def_id);
+        let name_str = &*name.as_str();
+
+        let llret_ty = self.layout_of(ret_ty).gcc_type(self, true);
+        let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout);
+
+        let simple = get_simple_intrinsic(self, name);
+        let llval =
+            match name {
+                _ if simple.is_some() => {
+                    // FIXME(antoyo): remove this cast when the API supports function.
+                    let func = unsafe { std::mem::transmute(simple.expect("simple")) };
+                    self.call(self.type_void(), func, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None)
+                },
+                sym::likely => {
+                    self.expect(args[0].immediate(), true)
+                }
+                sym::unlikely => {
+                    self.expect(args[0].immediate(), false)
+                }
+                kw::Try => {
+                    try_intrinsic(
+                        self,
+                        args[0].immediate(),
+                        args[1].immediate(),
+                        args[2].immediate(),
+                        llresult,
+                    );
+                    return;
+                }
+                sym::breakpoint => {
+                    unimplemented!();
+                }
+                sym::va_copy => {
+                    unimplemented!();
+                }
+                sym::va_arg => {
+                    unimplemented!();
+                }
+
+                sym::volatile_load | sym::unaligned_volatile_load => {
+                    let tp_ty = substs.type_at(0);
+                    let mut ptr = args[0].immediate();
+                    if let PassMode::Cast(ty) = fn_abi.ret.mode {
+                        ptr = self.pointercast(ptr, self.type_ptr_to(ty.gcc_type(self)));
+                    }
+                    let load = self.volatile_load(ptr.get_type(), ptr);
+                    // TODO(antoyo): set alignment.
+                    self.to_immediate(load, self.layout_of(tp_ty))
+                }
+                sym::volatile_store => {
+                    let dst = args[0].deref(self.cx());
+                    args[1].val.volatile_store(self, dst);
+                    return;
+                }
+                sym::unaligned_volatile_store => {
+                    let dst = args[0].deref(self.cx());
+                    args[1].val.unaligned_volatile_store(self, dst);
+                    return;
+                }
+                sym::prefetch_read_data
+                    | sym::prefetch_write_data
+                    | sym::prefetch_read_instruction
+                    | sym::prefetch_write_instruction => {
+                        unimplemented!();
+                    }
+                sym::ctlz
+                    | sym::ctlz_nonzero
+                    | sym::cttz
+                    | sym::cttz_nonzero
+                    | sym::ctpop
+                    | sym::bswap
+                    | sym::bitreverse
+                    | sym::rotate_left
+                    | sym::rotate_right
+                    | sym::saturating_add
+                    | sym::saturating_sub => {
+                        let ty = arg_tys[0];
+                        match int_type_width_signed(ty, self) {
+                            Some((width, signed)) => match name {
+                                sym::ctlz | sym::cttz => {
+                                    let func = self.current_func.borrow().expect("func");
+                                    let then_block = func.new_block("then");
+                                    let else_block = func.new_block("else");
+                                    let after_block = func.new_block("after");
+
+                                    let arg = args[0].immediate();
+                                    let result = func.new_local(None, arg.get_type(), "zeros");
+                                    let zero = self.cx.context.new_rvalue_zero(arg.get_type());
+                                    let cond = self.cx.context.new_comparison(None, ComparisonOp::Equals, arg, zero);
+                                    self.llbb().end_with_conditional(None, cond, then_block, else_block);
+
+                                    let zero_result = self.cx.context.new_rvalue_from_long(arg.get_type(), width as i64);
+                                    then_block.add_assignment(None, result, zero_result);
+                                    then_block.end_with_jump(None, after_block);
+
+                                    // NOTE: since jumps were added in a place
+                                    // count_leading_zeroes() does not expect, the current blocks
+                                    // in the state need to be updated.
+                                    *self.current_block.borrow_mut() = Some(else_block);
+                                    self.block = Some(else_block);
+
+                                    let zeros =
+                                        match name {
+                                            sym::ctlz => self.count_leading_zeroes(width, arg),
+                                            sym::cttz => self.count_trailing_zeroes(width, arg),
+                                            _ => unreachable!(),
+                                        };
+                                    else_block.add_assignment(None, result, zeros);
+                                    else_block.end_with_jump(None, after_block);
+
+                                    // NOTE: since jumps were added in a place rustc does not
+                                    // expect, the current blocks in the state need to be updated.
+                                    *self.current_block.borrow_mut() = Some(after_block);
+                                    self.block = Some(after_block);
+
+                                    result.to_rvalue()
+                                }
+                                sym::ctlz_nonzero => {
+                                    self.count_leading_zeroes(width, args[0].immediate())
+                                },
+                                sym::cttz_nonzero => {
+                                    self.count_trailing_zeroes(width, args[0].immediate())
+                                }
+                                sym::ctpop => self.pop_count(args[0].immediate()),
+                                sym::bswap => {
+                                    if width == 8 {
+                                        args[0].immediate() // byte swap a u8/i8 is just a no-op
+                                    }
+                                    else {
+                                        // TODO(antoyo): check if it's faster to use string literals and a
+                                        // match instead of format!.
+                                        let bswap = self.cx.context.get_builtin_function(&format!("__builtin_bswap{}", width));
+                                        let mut arg = args[0].immediate();
+                                        // FIXME(antoyo): this cast should not be necessary. Remove
+                                        // when having proper sized integer types.
+                                        let param_type = bswap.get_param(0).to_rvalue().get_type();
+                                        if param_type != arg.get_type() {
+                                            arg = self.bitcast(arg, param_type);
+                                        }
+                                        self.cx.context.new_call(None, bswap, &[arg])
+                                    }
+                                },
+                                sym::bitreverse => self.bit_reverse(width, args[0].immediate()),
+                                sym::rotate_left | sym::rotate_right => {
+                                    // TODO(antoyo): implement using algorithm from:
+                                    // https://blog.regehr.org/archives/1063
+                                    // for other platforms.
+                                    let is_left = name == sym::rotate_left;
+                                    let val = args[0].immediate();
+                                    let raw_shift = args[1].immediate();
+                                    if is_left {
+                                        self.rotate_left(val, raw_shift, width)
+                                    }
+                                    else {
+                                        self.rotate_right(val, raw_shift, width)
+                                    }
+                                },
+                                sym::saturating_add => {
+                                    self.saturating_add(args[0].immediate(), args[1].immediate(), signed, width)
+                                },
+                                sym::saturating_sub => {
+                                    self.saturating_sub(args[0].immediate(), args[1].immediate(), signed, width)
+                                },
+                                _ => bug!(),
+                            },
+                            None => {
+                                span_invalid_monomorphization_error(
+                                    tcx.sess,
+                                    span,
+                                    &format!(
+                                        "invalid monomorphization of `{}` intrinsic: \
+                                      expected basic integer type, found `{}`",
+                                      name, ty
+                                    ),
+                                );
+                                return;
+                            }
+                        }
+                    }
+
+                sym::raw_eq => {
+                    use rustc_target::abi::Abi::*;
+                    let tp_ty = substs.type_at(0);
+                    let layout = self.layout_of(tp_ty).layout;
+                    let _use_integer_compare = match layout.abi {
+                        Scalar(_) | ScalarPair(_, _) => true,
+                        Uninhabited | Vector { .. } => false,
+                        Aggregate { .. } => {
+                            // For rusty ABIs, small aggregates are actually passed
+                            // as `RegKind::Integer` (see `FnAbi::adjust_for_abi`),
+                            // so we re-use that same threshold here.
+                            layout.size <= self.data_layout().pointer_size * 2
+                        }
+                    };
+
+                    let a = args[0].immediate();
+                    let b = args[1].immediate();
+                    if layout.size.bytes() == 0 {
+                        self.const_bool(true)
+                    }
+                    /*else if use_integer_compare {
+                        let integer_ty = self.type_ix(layout.size.bits()); // FIXME(antoyo): LLVM creates an integer of 96 bits for [i32; 3], but gcc doesn't support this, so it creates an integer of 128 bits.
+                        let ptr_ty = self.type_ptr_to(integer_ty);
+                        let a_ptr = self.bitcast(a, ptr_ty);
+                        let a_val = self.load(integer_ty, a_ptr, layout.align.abi);
+                        let b_ptr = self.bitcast(b, ptr_ty);
+                        let b_val = self.load(integer_ty, b_ptr, layout.align.abi);
+                        self.icmp(IntPredicate::IntEQ, a_val, b_val)
+                    }*/
+                    else {
+                        let void_ptr_type = self.context.new_type::<*const ()>();
+                        let a_ptr = self.bitcast(a, void_ptr_type);
+                        let b_ptr = self.bitcast(b, void_ptr_type);
+                        let n = self.context.new_cast(None, self.const_usize(layout.size.bytes()), self.sizet_type);
+                        let builtin = self.context.get_builtin_function("memcmp");
+                        let cmp = self.context.new_call(None, builtin, &[a_ptr, b_ptr, n]);
+                        self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0))
+                    }
+                }
+
+                sym::black_box => {
+                    args[0].val.store(self, result);
+
+                    let block = self.llbb();
+                    let extended_asm = block.add_extended_asm(None, "");
+                    extended_asm.add_input_operand(None, "r", result.llval);
+                    extended_asm.add_clobber("memory");
+                    extended_asm.set_volatile_flag(true);
+                    
+                    // We have copied the value to `result` already.
+                    return;
+                }
+
+                _ if name_str.starts_with("simd_") => {
+                    match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
+                        Ok(llval) => llval,
+                        Err(()) => return,
+                    }
+                }
+
+                _ => bug!("unknown intrinsic '{}'", name),
+            };
+
+        if !fn_abi.ret.is_ignore() {
+            if let PassMode::Cast(ty) = fn_abi.ret.mode {
+                let ptr_llty = self.type_ptr_to(ty.gcc_type(self));
+                let ptr = self.pointercast(result.llval, ptr_llty);
+                self.store(llval, ptr, result.align);
+            }
+            else {
+                OperandRef::from_immediate_or_packed_pair(self, llval, result.layout)
+                    .val
+                    .store(self, result);
+            }
+        }
+    }
+
+    fn abort(&mut self) {
+        let func = self.context.get_builtin_function("abort");
+        let func: RValue<'gcc> = unsafe { std::mem::transmute(func) };
+        self.call(self.type_void(), func, &[], None);
+    }
+
+    fn assume(&mut self, value: Self::Value) {
+        // TODO(antoyo): switch to asumme when it exists.
+        // Or use something like this:
+        // #define __assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
+        self.expect(value, true);
+    }
+
+    fn expect(&mut self, cond: Self::Value, _expected: bool) -> Self::Value {
+        // TODO(antoyo)
+        cond
+    }
+
+    fn sideeffect(&mut self) {
+        // TODO(antoyo)
+    }
+
+    fn va_start(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+
+    fn va_end(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> {
+        unimplemented!();
+    }
+}
+
+impl<'a, 'gcc, 'tcx> ArgAbiMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
+    fn store_fn_arg(&mut self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, idx: &mut usize, dst: PlaceRef<'tcx, Self::Value>) {
+        arg_abi.store_fn_arg(self, idx, dst)
+    }
+
+    fn store_arg(&mut self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, val: RValue<'gcc>, dst: PlaceRef<'tcx, RValue<'gcc>>) {
+        arg_abi.store(self, val, dst)
+    }
+
+    fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> {
+        arg_abi.memory_ty(self)
+    }
+}
+
+pub trait ArgAbiExt<'gcc, 'tcx> {
+    fn memory_ty(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
+    fn store(&self, bx: &mut Builder<'_, 'gcc, 'tcx>, val: RValue<'gcc>, dst: PlaceRef<'tcx, RValue<'gcc>>);
+    fn store_fn_arg(&self, bx: &mut Builder<'_, 'gcc, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx, RValue<'gcc>>);
+}
+
+impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
+    /// Gets the LLVM type for a place of the original Rust type of
+    /// this argument/return, i.e., the result of `type_of::type_of`.
+    fn memory_ty(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+        self.layout.gcc_type(cx, true)
+    }
+
+    /// Stores a direct/indirect value described by this ArgAbi into a
+    /// place for the original Rust type of this argument/return.
+    /// Can be used for both storing formal arguments into Rust variables
+    /// or results of call/invoke instructions into their destinations.
+    fn store(&self, bx: &mut Builder<'_, 'gcc, 'tcx>, val: RValue<'gcc>, dst: PlaceRef<'tcx, RValue<'gcc>>) {
+        if self.is_ignore() {
+            return;
+        }
+        if self.is_sized_indirect() {
+            OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst)
+        }
+        else if self.is_unsized_indirect() {
+            bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
+        }
+        else if let PassMode::Cast(cast) = self.mode {
+            // FIXME(eddyb): Figure out when the simpler Store is safe, clang
+            // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
+            let can_store_through_cast_ptr = false;
+            if can_store_through_cast_ptr {
+                let cast_ptr_llty = bx.type_ptr_to(cast.gcc_type(bx));
+                let cast_dst = bx.pointercast(dst.llval, cast_ptr_llty);
+                bx.store(val, cast_dst, self.layout.align.abi);
+            }
+            else {
+                // The actual return type is a struct, but the ABI
+                // adaptation code has cast it into some scalar type.  The
+                // code that follows is the only reliable way I have
+                // found to do a transform like i64 -> {i32,i32}.
+                // Basically we dump the data onto the stack then memcpy it.
+                //
+                // Other approaches I tried:
+                // - Casting rust ret pointer to the foreign type and using Store
+                //   is (a) unsafe if size of foreign type > size of rust type and
+                //   (b) runs afoul of strict aliasing rules, yielding invalid
+                //   assembly under -O (specifically, the store gets removed).
+                // - Truncating foreign type to correct integral type and then
+                //   bitcasting to the struct type yields invalid cast errors.
+
+                // We instead thus allocate some scratch space...
+                let scratch_size = cast.size(bx);
+                let scratch_align = cast.align(bx);
+                let llscratch = bx.alloca(cast.gcc_type(bx), scratch_align);
+                bx.lifetime_start(llscratch, scratch_size);
+
+                // ... where we first store the value...
+                bx.store(val, llscratch, scratch_align);
+
+                // ... and then memcpy it to the intended destination.
+                bx.memcpy(
+                    dst.llval,
+                    self.layout.align.abi,
+                    llscratch,
+                    scratch_align,
+                    bx.const_usize(self.layout.size.bytes()),
+                    MemFlags::empty(),
+                );
+
+                bx.lifetime_end(llscratch, scratch_size);
+            }
+        }
+        else {
+            OperandValue::Immediate(val).store(bx, dst);
+        }
+    }
+
+    fn store_fn_arg<'a>(&self, bx: &mut Builder<'a, 'gcc, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx, RValue<'gcc>>) {
+        let mut next = || {
+            let val = bx.current_func().get_param(*idx as i32);
+            *idx += 1;
+            val.to_rvalue()
+        };
+        match self.mode {
+            PassMode::Ignore => {}
+            PassMode::Pair(..) => {
+                OperandValue::Pair(next(), next()).store(bx, dst);
+            }
+            PassMode::Indirect { extra_attrs: Some(_), .. } => {
+                OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst);
+            }
+            PassMode::Direct(_) | PassMode::Indirect { extra_attrs: None, .. } | PassMode::Cast(_) => {
+                let next_arg = next();
+                self.store(bx, next_arg.to_rvalue(), dst);
+            }
+        }
+    }
+}
+
+fn int_type_width_signed<'gcc, 'tcx>(ty: Ty<'tcx>, cx: &CodegenCx<'gcc, 'tcx>) -> Option<(u64, bool)> {
+    match ty.kind() {
+        ty::Int(t) => Some((
+            match t {
+                rustc_middle::ty::IntTy::Isize => u64::from(cx.tcx.sess.target.pointer_width),
+                rustc_middle::ty::IntTy::I8 => 8,
+                rustc_middle::ty::IntTy::I16 => 16,
+                rustc_middle::ty::IntTy::I32 => 32,
+                rustc_middle::ty::IntTy::I64 => 64,
+                rustc_middle::ty::IntTy::I128 => 128,
+            },
+            true,
+        )),
+        ty::Uint(t) => Some((
+            match t {
+                rustc_middle::ty::UintTy::Usize => u64::from(cx.tcx.sess.target.pointer_width),
+                rustc_middle::ty::UintTy::U8 => 8,
+                rustc_middle::ty::UintTy::U16 => 16,
+                rustc_middle::ty::UintTy::U32 => 32,
+                rustc_middle::ty::UintTy::U64 => 64,
+                rustc_middle::ty::UintTy::U128 => 128,
+            },
+            false,
+        )),
+        _ => None,
+    }
+}
+
+impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
+    fn bit_reverse(&mut self, width: u64, value: RValue<'gcc>) -> RValue<'gcc> {
+        let result_type = value.get_type();
+        let typ = result_type.to_unsigned(self.cx);
+
+        let value =
+            if result_type.is_signed(self.cx) {
+                self.context.new_bitcast(None, value, typ)
+            }
+            else {
+                value
+            };
+
+        let context = &self.cx.context;
+        let result =
+            match width {
+                8 => {
+                    // First step.
+                    let left = self.and(value, context.new_rvalue_from_int(typ, 0xF0));
+                    let left = self.lshr(left, context.new_rvalue_from_int(typ, 4));
+                    let right = self.and(value, context.new_rvalue_from_int(typ, 0x0F));
+                    let right = self.shl(right, context.new_rvalue_from_int(typ, 4));
+                    let step1 = self.or(left, right);
+
+                    // Second step.
+                    let left = self.and(step1, context.new_rvalue_from_int(typ, 0xCC));
+                    let left = self.lshr(left, context.new_rvalue_from_int(typ, 2));
+                    let right = self.and(step1, context.new_rvalue_from_int(typ, 0x33));
+                    let right = self.shl(right, context.new_rvalue_from_int(typ, 2));
+                    let step2 = self.or(left, right);
+
+                    // Third step.
+                    let left = self.and(step2, context.new_rvalue_from_int(typ, 0xAA));
+                    let left = self.lshr(left, context.new_rvalue_from_int(typ, 1));
+                    let right = self.and(step2, context.new_rvalue_from_int(typ, 0x55));
+                    let right = self.shl(right, context.new_rvalue_from_int(typ, 1));
+                    let step3 = self.or(left, right);
+
+                    step3
+                },
+                16 => {
+                    // First step.
+                    let left = self.and(value, context.new_rvalue_from_int(typ, 0x5555));
+                    let left = self.shl(left, context.new_rvalue_from_int(typ, 1));
+                    let right = self.and(value, context.new_rvalue_from_int(typ, 0xAAAA));
+                    let right = self.lshr(right, context.new_rvalue_from_int(typ, 1));
+                    let step1 = self.or(left, right);
+
+                    // Second step.
+                    let left = self.and(step1, context.new_rvalue_from_int(typ, 0x3333));
+                    let left = self.shl(left, context.new_rvalue_from_int(typ, 2));
+                    let right = self.and(step1, context.new_rvalue_from_int(typ, 0xCCCC));
+                    let right = self.lshr(right, context.new_rvalue_from_int(typ, 2));
+                    let step2 = self.or(left, right);
+
+                    // Third step.
+                    let left = self.and(step2, context.new_rvalue_from_int(typ, 0x0F0F));
+                    let left = self.shl(left, context.new_rvalue_from_int(typ, 4));
+                    let right = self.and(step2, context.new_rvalue_from_int(typ, 0xF0F0));
+                    let right = self.lshr(right, context.new_rvalue_from_int(typ, 4));
+                    let step3 = self.or(left, right);
+
+                    // Fourth step.
+                    let left = self.and(step3, context.new_rvalue_from_int(typ, 0x00FF));
+                    let left = self.shl(left, context.new_rvalue_from_int(typ, 8));
+                    let right = self.and(step3, context.new_rvalue_from_int(typ, 0xFF00));
+                    let right = self.lshr(right, context.new_rvalue_from_int(typ, 8));
+                    let step4 = self.or(left, right);
+
+                    step4
+                },
+                32 => {
+                    // TODO(antoyo): Refactor with other implementations.
+                    // First step.
+                    let left = self.and(value, context.new_rvalue_from_long(typ, 0x55555555));
+                    let left = self.shl(left, context.new_rvalue_from_long(typ, 1));
+                    let right = self.and(value, context.new_rvalue_from_long(typ, 0xAAAAAAAA));
+                    let right = self.lshr(right, context.new_rvalue_from_long(typ, 1));
+                    let step1 = self.or(left, right);
+
+                    // Second step.
+                    let left = self.and(step1, context.new_rvalue_from_long(typ, 0x33333333));
+                    let left = self.shl(left, context.new_rvalue_from_long(typ, 2));
+                    let right = self.and(step1, context.new_rvalue_from_long(typ, 0xCCCCCCCC));
+                    let right = self.lshr(right, context.new_rvalue_from_long(typ, 2));
+                    let step2 = self.or(left, right);
+
+                    // Third step.
+                    let left = self.and(step2, context.new_rvalue_from_long(typ, 0x0F0F0F0F));
+                    let left = self.shl(left, context.new_rvalue_from_long(typ, 4));
+                    let right = self.and(step2, context.new_rvalue_from_long(typ, 0xF0F0F0F0));
+                    let right = self.lshr(right, context.new_rvalue_from_long(typ, 4));
+                    let step3 = self.or(left, right);
+
+                    // Fourth step.
+                    let left = self.and(step3, context.new_rvalue_from_long(typ, 0x00FF00FF));
+                    let left = self.shl(left, context.new_rvalue_from_long(typ, 8));
+                    let right = self.and(step3, context.new_rvalue_from_long(typ, 0xFF00FF00));
+                    let right = self.lshr(right, context.new_rvalue_from_long(typ, 8));
+                    let step4 = self.or(left, right);
+
+                    // Fifth step.
+                    let left = self.and(step4, context.new_rvalue_from_long(typ, 0x0000FFFF));
+                    let left = self.shl(left, context.new_rvalue_from_long(typ, 16));
+                    let right = self.and(step4, context.new_rvalue_from_long(typ, 0xFFFF0000));
+                    let right = self.lshr(right, context.new_rvalue_from_long(typ, 16));
+                    let step5 = self.or(left, right);
+
+                    step5
+                },
+                64 => {
+                    // First step.
+                    let left = self.shl(value, context.new_rvalue_from_long(typ, 32));
+                    let right = self.lshr(value, context.new_rvalue_from_long(typ, 32));
+                    let step1 = self.or(left, right);
+
+                    // Second step.
+                    let left = self.and(step1, context.new_rvalue_from_long(typ, 0x0001FFFF0001FFFF));
+                    let left = self.shl(left, context.new_rvalue_from_long(typ, 15));
+                    let right = self.and(step1, context.new_rvalue_from_long(typ, 0xFFFE0000FFFE0000u64 as i64)); // TODO(antoyo): transmute the number instead?
+                    let right = self.lshr(right, context.new_rvalue_from_long(typ, 17));
+                    let step2 = self.or(left, right);
+
+                    // Third step.
+                    let left = self.lshr(step2, context.new_rvalue_from_long(typ, 10));
+                    let left = self.xor(step2, left);
+                    let temp = self.and(left, context.new_rvalue_from_long(typ, 0x003F801F003F801F));
+
+                    let left = self.shl(temp, context.new_rvalue_from_long(typ, 10));
+                    let left = self.or(temp, left);
+                    let step3 = self.xor(left, step2);
+
+                    // Fourth step.
+                    let left = self.lshr(step3, context.new_rvalue_from_long(typ, 4));
+                    let left = self.xor(step3, left);
+                    let temp = self.and(left, context.new_rvalue_from_long(typ, 0x0E0384210E038421));
+
+                    let left = self.shl(temp, context.new_rvalue_from_long(typ, 4));
+                    let left = self.or(temp, left);
+                    let step4 = self.xor(left, step3);
+
+                    // Fifth step.
+                    let left = self.lshr(step4, context.new_rvalue_from_long(typ, 2));
+                    let left = self.xor(step4, left);
+                    let temp = self.and(left, context.new_rvalue_from_long(typ, 0x2248884222488842));
+
+                    let left = self.shl(temp, context.new_rvalue_from_long(typ, 2));
+                    let left = self.or(temp, left);
+                    let step5 = self.xor(left, step4);
+
+                    step5
+                },
+                128 => {
+                    // TODO(antoyo): find a more efficient implementation?
+                    let sixty_four = self.context.new_rvalue_from_long(typ, 64);
+                    let high = self.context.new_cast(None, value >> sixty_four, self.u64_type);
+                    let low = self.context.new_cast(None, value, self.u64_type);
+
+                    let reversed_high = self.bit_reverse(64, high);
+                    let reversed_low = self.bit_reverse(64, low);
+
+                    let new_low = self.context.new_cast(None, reversed_high, typ);
+                    let new_high = self.context.new_cast(None, reversed_low, typ) << sixty_four;
+
+                    new_low | new_high
+                },
+                _ => {
+                    panic!("cannot bit reverse with width = {}", width);
+                },
+            };
+
+        self.context.new_bitcast(None, result, result_type)
+    }
+
+    fn count_leading_zeroes(&self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): use width?
+        let arg_type = arg.get_type();
+        let count_leading_zeroes =
+            if arg_type.is_uint(&self.cx) {
+                "__builtin_clz"
+            }
+            else if arg_type.is_ulong(&self.cx) {
+                "__builtin_clzl"
+            }
+            else if arg_type.is_ulonglong(&self.cx) {
+                "__builtin_clzll"
+            }
+            else if width == 128 {
+                // Algorithm from: https://stackoverflow.com/a/28433850/389119
+                let array_type = self.context.new_array_type(None, arg_type, 3);
+                let result = self.current_func()
+                    .new_local(None, array_type, "count_loading_zeroes_results");
+
+                let sixty_four = self.context.new_rvalue_from_long(arg_type, 64);
+                let high = self.context.new_cast(None, arg >> sixty_four, self.u64_type);
+                let low = self.context.new_cast(None, arg, self.u64_type);
+
+                let zero = self.context.new_rvalue_zero(self.usize_type);
+                let one = self.context.new_rvalue_one(self.usize_type);
+                let two = self.context.new_rvalue_from_long(self.usize_type, 2);
+
+                let clzll = self.context.get_builtin_function("__builtin_clzll");
+
+                let first_elem = self.context.new_array_access(None, result, zero);
+                let first_value = self.context.new_cast(None, self.context.new_call(None, clzll, &[high]), arg_type);
+                self.llbb()
+                    .add_assignment(None, first_elem, first_value);
+
+                let second_elem = self.context.new_array_access(None, result, one);
+                let second_value = self.context.new_cast(None, self.context.new_call(None, clzll, &[low]), arg_type) + sixty_four;
+                self.llbb()
+                    .add_assignment(None, second_elem, second_value);
+
+                let third_elem = self.context.new_array_access(None, result, two);
+                let third_value = self.context.new_rvalue_from_long(arg_type, 128);
+                self.llbb()
+                    .add_assignment(None, third_elem, third_value);
+
+                let not_high = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, high);
+                let not_low = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, low);
+                let not_low_and_not_high = not_low & not_high;
+                let index = not_high + not_low_and_not_high;
+
+                let res = self.context.new_array_access(None, result, index);
+
+                return self.context.new_cast(None, res, arg_type);
+            }
+            else {
+                let count_leading_zeroes = self.context.get_builtin_function("__builtin_clz");
+                let arg = self.context.new_cast(None, arg, self.uint_type);
+                let diff = self.int_width(self.uint_type) - self.int_width(arg_type);
+                let diff = self.context.new_rvalue_from_long(self.int_type, diff);
+                let res = self.context.new_call(None, count_leading_zeroes, &[arg]) - diff;
+                return self.context.new_cast(None, res, arg_type);
+            };
+        let count_leading_zeroes = self.context.get_builtin_function(count_leading_zeroes);
+        let res = self.context.new_call(None, count_leading_zeroes, &[arg]);
+        self.context.new_cast(None, res, arg_type)
+    }
+
+    fn count_trailing_zeroes(&self, _width: u64, arg: RValue<'gcc>) -> RValue<'gcc> {
+        let result_type = arg.get_type();
+        let arg =
+            if result_type.is_signed(self.cx) {
+                let new_type = result_type.to_unsigned(self.cx);
+                self.context.new_bitcast(None, arg, new_type)
+            }
+            else {
+                arg
+            };
+        let arg_type = arg.get_type();
+        let (count_trailing_zeroes, expected_type) =
+            if arg_type.is_uchar(&self.cx) || arg_type.is_ushort(&self.cx) || arg_type.is_uint(&self.cx) {
+                // NOTE: we don't need to & 0xFF for uchar because the result is undefined on zero.
+                ("__builtin_ctz", self.cx.uint_type)
+            }
+            else if arg_type.is_ulong(&self.cx) {
+                ("__builtin_ctzl", self.cx.ulong_type)
+            }
+            else if arg_type.is_ulonglong(&self.cx) {
+                ("__builtin_ctzll", self.cx.ulonglong_type)
+            }
+            else if arg_type.is_u128(&self.cx) {
+                // Adapted from the algorithm to count leading zeroes from: https://stackoverflow.com/a/28433850/389119
+                let array_type = self.context.new_array_type(None, arg_type, 3);
+                let result = self.current_func()
+                    .new_local(None, array_type, "count_loading_zeroes_results");
+
+                let sixty_four = self.context.new_rvalue_from_long(arg_type, 64);
+                let high = self.context.new_cast(None, arg >> sixty_four, self.u64_type);
+                let low = self.context.new_cast(None, arg, self.u64_type);
+
+                let zero = self.context.new_rvalue_zero(self.usize_type);
+                let one = self.context.new_rvalue_one(self.usize_type);
+                let two = self.context.new_rvalue_from_long(self.usize_type, 2);
+
+                let ctzll = self.context.get_builtin_function("__builtin_ctzll");
+
+                let first_elem = self.context.new_array_access(None, result, zero);
+                let first_value = self.context.new_cast(None, self.context.new_call(None, ctzll, &[low]), arg_type);
+                self.llbb()
+                    .add_assignment(None, first_elem, first_value);
+
+                let second_elem = self.context.new_array_access(None, result, one);
+                let second_value = self.context.new_cast(None, self.context.new_call(None, ctzll, &[high]), arg_type) + sixty_four;
+                self.llbb()
+                    .add_assignment(None, second_elem, second_value);
+
+                let third_elem = self.context.new_array_access(None, result, two);
+                let third_value = self.context.new_rvalue_from_long(arg_type, 128);
+                self.llbb()
+                    .add_assignment(None, third_elem, third_value);
+
+                let not_low = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, low);
+                let not_high = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, high);
+                let not_low_and_not_high = not_low & not_high;
+                let index = not_low + not_low_and_not_high;
+
+                let res = self.context.new_array_access(None, result, index);
+
+                return self.context.new_bitcast(None, res, result_type);
+            }
+            else {
+                unimplemented!("count_trailing_zeroes for {:?}", arg_type);
+            };
+        let count_trailing_zeroes = self.context.get_builtin_function(count_trailing_zeroes);
+        let arg =
+            if arg_type != expected_type {
+                self.context.new_cast(None, arg, expected_type)
+            }
+            else {
+                arg
+            };
+        let res = self.context.new_call(None, count_trailing_zeroes, &[arg]);
+        self.context.new_bitcast(None, res, result_type)
+    }
+
+    fn int_width(&self, typ: Type<'gcc>) -> i64 {
+        self.cx.int_width(typ) as i64
+    }
+
+    fn pop_count(&self, value: RValue<'gcc>) -> RValue<'gcc> {
+        // TODO(antoyo): use the optimized version with fewer operations.
+        let result_type = value.get_type();
+        let value_type = result_type.to_unsigned(self.cx);
+
+        let value =
+            if result_type.is_signed(self.cx) {
+                self.context.new_bitcast(None, value, value_type)
+            }
+            else {
+                value
+            };
+
+        if value_type.is_u128(&self.cx) {
+            // TODO(antoyo): implement in the normal algorithm below to have a more efficient
+            // implementation (that does not require a call to __popcountdi2).
+            let popcount = self.context.get_builtin_function("__builtin_popcountll");
+            let sixty_four = self.context.new_rvalue_from_long(value_type, 64);
+            let high = self.context.new_cast(None, value >> sixty_four, self.cx.ulonglong_type);
+            let high = self.context.new_call(None, popcount, &[high]);
+            let low = self.context.new_cast(None, value, self.cx.ulonglong_type);
+            let low = self.context.new_call(None, popcount, &[low]);
+            let res = high + low;
+            return self.context.new_bitcast(None, res, result_type);
+        }
+
+        // First step.
+        let mask = self.context.new_rvalue_from_long(value_type, 0x5555555555555555);
+        let left = value & mask;
+        let shifted = value >> self.context.new_rvalue_from_int(value_type, 1);
+        let right = shifted & mask;
+        let value = left + right;
+
+        // Second step.
+        let mask = self.context.new_rvalue_from_long(value_type, 0x3333333333333333);
+        let left = value & mask;
+        let shifted = value >> self.context.new_rvalue_from_int(value_type, 2);
+        let right = shifted & mask;
+        let value = left + right;
+
+        // Third step.
+        let mask = self.context.new_rvalue_from_long(value_type, 0x0F0F0F0F0F0F0F0F);
+        let left = value & mask;
+        let shifted = value >> self.context.new_rvalue_from_int(value_type, 4);
+        let right = shifted & mask;
+        let value = left + right;
+
+        if value_type.is_u8(&self.cx) {
+            return self.context.new_bitcast(None, value, result_type);
+        }
+
+        // Fourth step.
+        let mask = self.context.new_rvalue_from_long(value_type, 0x00FF00FF00FF00FF);
+        let left = value & mask;
+        let shifted = value >> self.context.new_rvalue_from_int(value_type, 8);
+        let right = shifted & mask;
+        let value = left + right;
+
+        if value_type.is_u16(&self.cx) {
+            return self.context.new_bitcast(None, value, result_type);
+        }
+
+        // Fifth step.
+        let mask = self.context.new_rvalue_from_long(value_type, 0x0000FFFF0000FFFF);
+        let left = value & mask;
+        let shifted = value >> self.context.new_rvalue_from_int(value_type, 16);
+        let right = shifted & mask;
+        let value = left + right;
+
+        if value_type.is_u32(&self.cx) {
+            return self.context.new_bitcast(None, value, result_type);
+        }
+
+        // Sixth step.
+        let mask = self.context.new_rvalue_from_long(value_type, 0x00000000FFFFFFFF);
+        let left = value & mask;
+        let shifted = value >> self.context.new_rvalue_from_int(value_type, 32);
+        let right = shifted & mask;
+        let value = left + right;
+
+        self.context.new_bitcast(None, value, result_type)
+    }
+
+    // Algorithm from: https://blog.regehr.org/archives/1063
+    fn rotate_left(&mut self, value: RValue<'gcc>, shift: RValue<'gcc>, width: u64) -> RValue<'gcc> {
+        let max = self.context.new_rvalue_from_long(shift.get_type(), width as i64);
+        let shift = shift % max;
+        let lhs = self.shl(value, shift);
+        let result_and =
+            self.and(
+                self.context.new_unary_op(None, UnaryOp::Minus, shift.get_type(), shift),
+                self.context.new_rvalue_from_long(shift.get_type(), width as i64 - 1),
+            );
+        let rhs = self.lshr(value, result_and);
+        self.or(lhs, rhs)
+    }
+
+    // Algorithm from: https://blog.regehr.org/archives/1063
+    fn rotate_right(&mut self, value: RValue<'gcc>, shift: RValue<'gcc>, width: u64) -> RValue<'gcc> {
+        let max = self.context.new_rvalue_from_long(shift.get_type(), width as i64);
+        let shift = shift % max;
+        let lhs = self.lshr(value, shift);
+        let result_and =
+            self.and(
+                self.context.new_unary_op(None, UnaryOp::Minus, shift.get_type(), shift),
+                self.context.new_rvalue_from_long(shift.get_type(), width as i64 - 1),
+            );
+        let rhs = self.shl(value, result_and);
+        self.or(lhs, rhs)
+    }
+
+    fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> {
+        let func = self.current_func.borrow().expect("func");
+
+        if signed {
+            // Algorithm from: https://stackoverflow.com/a/56531252/389119
+            let after_block = func.new_block("after");
+            let func_name =
+                match width {
+                    8 => "__builtin_add_overflow",
+                    16 => "__builtin_add_overflow",
+                    32 => "__builtin_sadd_overflow",
+                    64 => "__builtin_saddll_overflow",
+                    128 => "__builtin_add_overflow",
+                    _ => unreachable!(),
+                };
+            let overflow_func = self.context.get_builtin_function(func_name);
+            let result_type = lhs.get_type();
+            let res = func.new_local(None, result_type, "saturating_sum");
+            let overflow = self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None);
+
+            let then_block = func.new_block("then");
+
+            let unsigned_type = self.context.new_int_type(width as i32 / 8, false);
+            let shifted = self.context.new_cast(None, lhs, unsigned_type) >> self.context.new_rvalue_from_int(unsigned_type, width as i32 - 1);
+            let uint_max = self.context.new_unary_op(None, UnaryOp::BitwiseNegate, unsigned_type,
+                self.context.new_rvalue_from_int(unsigned_type, 0)
+            );
+            let int_max = uint_max >> self.context.new_rvalue_one(unsigned_type);
+            then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type));
+            then_block.end_with_jump(None, after_block);
+
+            self.llbb().end_with_conditional(None, overflow, then_block, after_block);
+
+            // NOTE: since jumps were added in a place rustc does not
+            // expect, the current blocks in the state need to be updated.
+            *self.current_block.borrow_mut() = Some(after_block);
+            self.block = Some(after_block);
+
+            res.to_rvalue()
+        }
+        else {
+            // Algorithm from: http://locklessinc.com/articles/sat_arithmetic/
+            let res = lhs + rhs;
+            let res_type = res.get_type();
+            let cond = self.context.new_comparison(None, ComparisonOp::LessThan, res, lhs);
+            let value = self.context.new_unary_op(None, UnaryOp::Minus, res_type, self.context.new_cast(None, cond, res_type));
+            res | value
+        }
+    }
+
+    // Algorithm from: https://locklessinc.com/articles/sat_arithmetic/
+    fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> {
+        if signed {
+            // Also based on algorithm from: https://stackoverflow.com/a/56531252/389119
+            let func_name =
+                match width {
+                    8 => "__builtin_sub_overflow",
+                    16 => "__builtin_sub_overflow",
+                    32 => "__builtin_ssub_overflow",
+                    64 => "__builtin_ssubll_overflow",
+                    128 => "__builtin_sub_overflow",
+                    _ => unreachable!(),
+                };
+            let overflow_func = self.context.get_builtin_function(func_name);
+            let result_type = lhs.get_type();
+            let func = self.current_func.borrow().expect("func");
+            let res = func.new_local(None, result_type, "saturating_diff");
+            let overflow = self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None);
+
+            let then_block = func.new_block("then");
+            let after_block = func.new_block("after");
+
+            let unsigned_type = self.context.new_int_type(width as i32 / 8, false);
+            let shifted = self.context.new_cast(None, lhs, unsigned_type) >> self.context.new_rvalue_from_int(unsigned_type, width as i32 - 1);
+            let uint_max = self.context.new_unary_op(None, UnaryOp::BitwiseNegate, unsigned_type,
+                self.context.new_rvalue_from_int(unsigned_type, 0)
+            );
+            let int_max = uint_max >> self.context.new_rvalue_one(unsigned_type);
+            then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type));
+            then_block.end_with_jump(None, after_block);
+
+            self.llbb().end_with_conditional(None, overflow, then_block, after_block);
+
+            // NOTE: since jumps were added in a place rustc does not
+            // expect, the current blocks in the state need to be updated.
+            *self.current_block.borrow_mut() = Some(after_block);
+            self.block = Some(after_block);
+
+            res.to_rvalue()
+        }
+        else {
+            let res = lhs - rhs;
+            let comparison = self.context.new_comparison(None, ComparisonOp::LessThanEquals, res, lhs);
+            let comparison = self.context.new_cast(None, comparison, lhs.get_type());
+            let unary_op = self.context.new_unary_op(None, UnaryOp::Minus, comparison.get_type(), comparison);
+            self.and(res, unary_op)
+        }
+    }
+}
+
+fn try_intrinsic<'gcc, 'tcx>(bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, dest: RValue<'gcc>) {
+    if bx.sess().panic_strategy() == PanicStrategy::Abort {
+        bx.call(bx.type_void(), try_func, &[data], None);
+        // Return 0 unconditionally from the intrinsic call;
+        // we can never unwind.
+        let ret_align = bx.tcx.data_layout.i32_align.abi;
+        bx.store(bx.const_i32(0), dest, ret_align);
+    }
+    else if wants_msvc_seh(bx.sess()) {
+        unimplemented!();
+    }
+    else {
+        unimplemented!();
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
new file mode 100644 (file)
index 0000000..26a4221
--- /dev/null
@@ -0,0 +1,167 @@
+use gccjit::{RValue, Type};
+use rustc_codegen_ssa::base::compare_simd_types;
+use rustc_codegen_ssa::common::{TypeKind, span_invalid_monomorphization_error};
+use rustc_codegen_ssa::mir::operand::OperandRef;
+use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods};
+use rustc_hir as hir;
+use rustc_middle::span_bug;
+use rustc_middle::ty::layout::HasTyCtxt;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{Span, Symbol, sym};
+
+use crate::builder::Builder;
+
+pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, name: Symbol, callee_ty: Ty<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, span: Span) -> Result<RValue<'gcc>, ()> {
+    // macros for error handling:
+    macro_rules! emit_error {
+        ($msg: tt) => {
+            emit_error!($msg, )
+        };
+        ($msg: tt, $($fmt: tt)*) => {
+            span_invalid_monomorphization_error(
+                bx.sess(), span,
+                &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg),
+                         name, $($fmt)*));
+        }
+    }
+
+    macro_rules! return_error {
+        ($($fmt: tt)*) => {
+            {
+                emit_error!($($fmt)*);
+                return Err(());
+            }
+        }
+    }
+
+    macro_rules! require {
+        ($cond: expr, $($fmt: tt)*) => {
+            if !$cond {
+                return_error!($($fmt)*);
+            }
+        };
+    }
+
+    macro_rules! require_simd {
+        ($ty: expr, $position: expr) => {
+            require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty)
+        };
+    }
+
+    let tcx = bx.tcx();
+    let sig =
+        tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), callee_ty.fn_sig(tcx));
+    let arg_tys = sig.inputs();
+    let name_str = &*name.as_str();
+
+    // every intrinsic below takes a SIMD vector as its first argument
+    require_simd!(arg_tys[0], "input");
+    let in_ty = arg_tys[0];
+
+    let comparison = match name {
+        sym::simd_eq => Some(hir::BinOpKind::Eq),
+        sym::simd_ne => Some(hir::BinOpKind::Ne),
+        sym::simd_lt => Some(hir::BinOpKind::Lt),
+        sym::simd_le => Some(hir::BinOpKind::Le),
+        sym::simd_gt => Some(hir::BinOpKind::Gt),
+        sym::simd_ge => Some(hir::BinOpKind::Ge),
+        _ => None,
+    };
+
+    let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx());
+    if let Some(cmp_op) = comparison {
+        require_simd!(ret_ty, "return");
+
+        let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
+        require!(
+            in_len == out_len,
+            "expected return type with length {} (same as input type `{}`), \
+             found `{}` with length {}",
+            in_len,
+            in_ty,
+            ret_ty,
+            out_len
+        );
+        require!(
+            bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
+            "expected return type with integer elements, found `{}` with non-integer `{}`",
+            ret_ty,
+            out_ty
+        );
+
+        return Ok(compare_simd_types(
+            bx,
+            args[0].immediate(),
+            args[1].immediate(),
+            in_elem,
+            llret_ty,
+            cmp_op,
+        ));
+    }
+
+    if let Some(stripped) = name_str.strip_prefix("simd_shuffle") {
+        let n: u64 = stripped.parse().unwrap_or_else(|_| {
+            span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?")
+        });
+
+        require_simd!(ret_ty, "return");
+
+        let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
+        require!(
+            out_len == n,
+            "expected return type of length {}, found `{}` with length {}",
+            n,
+            ret_ty,
+            out_len
+        );
+        require!(
+            in_elem == out_ty,
+            "expected return element type `{}` (element of input `{}`), \
+             found `{}` with element type `{}`",
+            in_elem,
+            in_ty,
+            ret_ty,
+            out_ty
+        );
+
+        let vector = args[2].immediate();
+
+        return Ok(bx.shuffle_vector(
+            args[0].immediate(),
+            args[1].immediate(),
+            vector,
+        ));
+    }
+
+    macro_rules! arith_binary {
+        ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
+            $(if name == sym::$name {
+                match in_elem.kind() {
+                    $($(ty::$p(_))|* => {
+                        return Ok(bx.$call(args[0].immediate(), args[1].immediate()))
+                    })*
+                    _ => {},
+                }
+                require!(false,
+                         "unsupported operation on `{}` with element `{}`",
+                         in_ty,
+                         in_elem)
+            })*
+        }
+    }
+
+    arith_binary! {
+        simd_add: Uint, Int => add, Float => fadd;
+        simd_sub: Uint, Int => sub, Float => fsub;
+        simd_mul: Uint, Int => mul, Float => fmul;
+        simd_div: Uint => udiv, Int => sdiv, Float => fdiv;
+        simd_rem: Uint => urem, Int => srem, Float => frem;
+        simd_shl: Uint, Int => shl;
+        simd_shr: Uint => lshr, Int => ashr;
+        simd_and: Uint, Int => and;
+        simd_or: Uint, Int => or; // FIXME(antoyo): calling `or` might not work on vectors.
+        simd_xor: Uint, Int => xor;
+    }
+
+    unimplemented!("simd {}", name);
+}
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
new file mode 100644 (file)
index 0000000..629003d
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * TODO(antoyo): support #[inline] attributes.
+ * TODO(antoyo): support LTO.
+ *
+ * TODO(antoyo): remove the patches.
+ */
+
+#![feature(rustc_private, decl_macro, associated_type_bounds, never_type, trusted_len)]
+#![allow(broken_intra_doc_links)]
+#![recursion_limit="256"]
+#![warn(rust_2018_idioms)]
+#![warn(unused_lifetimes)]
+
+extern crate rustc_ast;
+extern crate rustc_codegen_ssa;
+extern crate rustc_data_structures;
+extern crate rustc_errors;
+extern crate rustc_hir;
+extern crate rustc_metadata;
+extern crate rustc_middle;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_symbol_mangling;
+extern crate rustc_target;
+extern crate snap;
+
+// This prevents duplicating functions and statics that are already part of the host rustc process.
+#[allow(unused_extern_crates)]
+extern crate rustc_driver;
+
+mod abi;
+mod allocator;
+mod archive;
+mod asm;
+mod back;
+mod base;
+mod builder;
+mod callee;
+mod common;
+mod consts;
+mod context;
+mod coverageinfo;
+mod debuginfo;
+mod declare;
+mod intrinsic;
+mod mono_item;
+mod type_;
+mod type_of;
+
+use std::any::Any;
+use std::sync::Arc;
+
+use gccjit::{Context, OptimizationLevel};
+use rustc_ast::expand::allocator::AllocatorKind;
+use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
+use rustc_codegen_ssa::base::codegen_crate;
+use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryFn};
+use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
+use rustc_codegen_ssa::target_features::supported_target_features;
+use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{ErrorReported, Handler};
+use rustc_metadata::EncodedMetadata;
+use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
+use rustc_middle::ty::TyCtxt;
+use rustc_session::config::{Lto, OptLevel, OutputFilenames};
+use rustc_session::Session;
+use rustc_span::Symbol;
+use rustc_span::fatal_error::FatalError;
+
+pub struct PrintOnPanic<F: Fn() -> String>(pub F);
+
+impl<F: Fn() -> String> Drop for PrintOnPanic<F> {
+    fn drop(&mut self) {
+        if ::std::thread::panicking() {
+            println!("{}", (self.0)());
+        }
+    }
+}
+
+#[derive(Clone)]
+pub struct GccCodegenBackend;
+
+impl CodegenBackend for GccCodegenBackend {
+    fn init(&self, sess: &Session) {
+        if sess.lto() != Lto::No {
+            sess.warn("LTO is not supported. You may get a linker error.");
+        }
+    }
+
+    fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool) -> Box<dyn Any> {
+        let target_cpu = target_cpu(tcx.sess);
+        let res = codegen_crate(self.clone(), tcx, target_cpu.to_string(), metadata, need_metadata_module);
+
+        rustc_symbol_mangling::test::report_symbol_names(tcx);
+
+        Box::new(res)
+    }
+
+    fn join_codegen(&self, ongoing_codegen: Box<dyn Any>, sess: &Session) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorReported> {
+        let (codegen_results, work_products) = ongoing_codegen
+            .downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<GccCodegenBackend>>()
+            .expect("Expected GccCodegenBackend's OngoingCodegen, found Box<Any>")
+            .join(sess);
+
+        Ok((codegen_results, work_products))
+    }
+
+    fn link(&self, sess: &Session, codegen_results: CodegenResults, outputs: &OutputFilenames) -> Result<(), ErrorReported> {
+        use rustc_codegen_ssa::back::link::link_binary;
+
+        link_binary::<crate::archive::ArArchiveBuilder<'_>>(
+            sess,
+            &codegen_results,
+            outputs,
+        )
+    }
+
+    fn target_features(&self, sess: &Session) -> Vec<Symbol> {
+        target_features(sess)
+    }
+}
+
+impl ExtraBackendMethods for GccCodegenBackend {
+    fn new_metadata<'tcx>(&self, _tcx: TyCtxt<'tcx>, _mod_name: &str) -> Self::Module {
+        GccContext {
+            context: Context::default(),
+        }
+    }
+
+    fn write_compressed_metadata<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: &EncodedMetadata, gcc_module: &mut Self::Module) {
+        base::write_compressed_metadata(tcx, metadata, gcc_module)
+    }
+
+    fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, mods: &mut Self::Module, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) {
+        unsafe { allocator::codegen(tcx, mods, module_name, kind, has_alloc_error_handler) }
+    }
+
+    fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
+        base::compile_codegen_unit(tcx, cgu_name)
+    }
+
+    fn target_machine_factory(&self, _sess: &Session, _opt_level: OptLevel) -> TargetMachineFactoryFn<Self> {
+        // TODO(antoyo): set opt level.
+        Arc::new(|_| {
+            Ok(())
+        })
+    }
+
+    fn target_cpu<'b>(&self, _sess: &'b Session) -> &'b str {
+        unimplemented!();
+    }
+
+    fn tune_cpu<'b>(&self, _sess: &'b Session) -> Option<&'b str> {
+        None
+        // TODO(antoyo)
+    }
+}
+
+pub struct ModuleBuffer;
+
+impl ModuleBufferMethods for ModuleBuffer {
+    fn data(&self) -> &[u8] {
+        unimplemented!();
+    }
+}
+
+pub struct ThinBuffer;
+
+impl ThinBufferMethods for ThinBuffer {
+    fn data(&self) -> &[u8] {
+        unimplemented!();
+    }
+}
+
+pub struct GccContext {
+    context: Context<'static>,
+}
+
+unsafe impl Send for GccContext {}
+// FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". Try to disable it here.
+unsafe impl Sync for GccContext {}
+
+impl WriteBackendMethods for GccCodegenBackend {
+    type Module = GccContext;
+    type TargetMachine = ();
+    type ModuleBuffer = ModuleBuffer;
+    type Context = ();
+    type ThinData = ();
+    type ThinBuffer = ThinBuffer;
+
+    fn run_fat_lto(_cgcx: &CodegenContext<Self>, mut modules: Vec<FatLTOInput<Self>>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> {
+        // TODO(antoyo): implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins.
+        // NOTE: implemented elsewhere.
+        // TODO: what is implemented elsewhere ^ ?
+        let module =
+            match modules.remove(0) {
+                FatLTOInput::InMemory(module) => module,
+                FatLTOInput::Serialized { .. } => {
+                    unimplemented!();
+                }
+            };
+        Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: vec![] })
+    }
+
+    fn run_thin_lto(_cgcx: &CodegenContext<Self>, _modules: Vec<(String, Self::ThinBuffer)>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> {
+        unimplemented!();
+    }
+
+    fn print_pass_timings(&self) {
+        unimplemented!();
+    }
+
+    unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> {
+        module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level));
+        Ok(())
+    }
+
+    unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: &mut ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
+        unimplemented!();
+    }
+
+    unsafe fn codegen(cgcx: &CodegenContext<Self>, diag_handler: &Handler, module: ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<CompiledModule, FatalError> {
+        back::write::codegen(cgcx, diag_handler, module, config)
+    }
+
+    fn prepare_thin(_module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer) {
+        unimplemented!();
+    }
+
+    fn serialize_module(_module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
+        unimplemented!();
+    }
+
+    fn run_lto_pass_manager(_cgcx: &CodegenContext<Self>, _module: &ModuleCodegen<Self::Module>, _config: &ModuleConfig, _thin: bool) -> Result<(), FatalError> {
+        // TODO(antoyo)
+        Ok(())
+    }
+
+    fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
+        back::write::link(cgcx, diag_handler, modules)
+    }
+}
+
+/// This is the entrypoint for a hot plugged rustc_codegen_gccjit
+#[no_mangle]
+pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
+    Box::new(GccCodegenBackend)
+}
+
+fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
+    match optlevel {
+        None => OptimizationLevel::None,
+        Some(level) => {
+            match level {
+                OptLevel::No => OptimizationLevel::None,
+                OptLevel::Less => OptimizationLevel::Limited,
+                OptLevel::Default => OptimizationLevel::Standard,
+                OptLevel::Aggressive => OptimizationLevel::Aggressive,
+                OptLevel::Size | OptLevel::SizeMin => OptimizationLevel::Limited,
+            }
+        },
+    }
+}
+
+fn handle_native(name: &str) -> &str {
+    if name != "native" {
+        return name;
+    }
+
+    unimplemented!();
+}
+
+pub fn target_cpu(sess: &Session) -> &str {
+    let name = sess.opts.cg.target_cpu.as_ref().unwrap_or(&sess.target.cpu);
+    handle_native(name)
+}
+
+pub fn target_features(sess: &Session) -> Vec<Symbol> {
+    supported_target_features(sess)
+        .iter()
+        .filter_map(
+            |&(feature, gate)| {
+                if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None }
+            },
+        )
+        .filter(|_feature| {
+            // TODO(antoyo): implement a way to get enabled feature in libgccjit.
+            false
+        })
+        .map(|feature| Symbol::intern(feature))
+        .collect()
+}
diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs
new file mode 100644 (file)
index 0000000..e21d40b
--- /dev/null
@@ -0,0 +1,38 @@
+use rustc_codegen_ssa::traits::PreDefineMethods;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
+use rustc_middle::mir::mono::{Linkage, Visibility};
+use rustc_middle::ty::{self, Instance, TypeFoldable};
+use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
+use rustc_span::def_id::DefId;
+
+use crate::base;
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn predefine_static(&self, def_id: DefId, _linkage: Linkage, _visibility: Visibility, symbol_name: &str) {
+        let attrs = self.tcx.codegen_fn_attrs(def_id);
+        let instance = Instance::mono(self.tcx, def_id);
+        let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all());
+        let gcc_type = self.layout_of(ty).gcc_type(self, true);
+
+        let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL);
+        let global = self.define_global(symbol_name, gcc_type, is_tls, attrs.link_section);
+
+        // TODO(antoyo): set linkage and visibility.
+        self.instances.borrow_mut().insert(instance, global);
+    }
+
+    fn predefine_fn(&self, instance: Instance<'tcx>, linkage: Linkage, _visibility: Visibility, symbol_name: &str) {
+        assert!(!instance.substs.needs_infer());
+
+        let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
+        self.linkage.set(base::linkage_to_gcc(linkage));
+        let _decl = self.declare_fn(symbol_name, &fn_abi);
+        //let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
+
+        // TODO(antoyo): call set_link_section() to allow initializing argc/argv.
+        // TODO(antoyo): set unique comdat.
+        // TODO(antoyo): use inline attribute from there in linkage.set() above.
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs
new file mode 100644 (file)
index 0000000..3545e1b
--- /dev/null
@@ -0,0 +1,282 @@
+use std::convert::TryInto;
+
+use gccjit::{RValue, Struct, Type};
+use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods};
+use rustc_codegen_ssa::common::TypeKind;
+use rustc_middle::bug;
+use rustc_middle::ty::layout::TyAndLayout;
+use rustc_target::abi::{AddressSpace, Align, Integer, Size};
+
+use crate::common::TypeReflection;
+use crate::context::CodegenCx;
+use crate::type_of::LayoutGccExt;
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+    pub fn type_ix(&self, num_bits: u64) -> Type<'gcc> {
+        // gcc only supports 1, 2, 4 or 8-byte integers.
+        // FIXME(antoyo): this is misleading to use the next power of two as rustc_codegen_ssa
+        // sometimes use 96-bit numbers and the following code will give an integer of a different
+        // size.
+        let bytes = (num_bits / 8).next_power_of_two() as i32;
+        match bytes {
+            1 => self.i8_type,
+            2 => self.i16_type,
+            4 => self.i32_type,
+            8 => self.i64_type,
+            16 => self.i128_type,
+            _ => panic!("unexpected num_bits: {}", num_bits),
+        }
+    }
+
+    pub fn type_void(&self) -> Type<'gcc> {
+        self.context.new_type::<()>()
+    }
+
+    pub fn type_size_t(&self) -> Type<'gcc> {
+        self.context.new_type::<usize>()
+    }
+
+    pub fn type_u8(&self) -> Type<'gcc> {
+        self.u8_type
+    }
+
+    pub fn type_u16(&self) -> Type<'gcc> {
+        self.u16_type
+    }
+
+    pub fn type_u32(&self) -> Type<'gcc> {
+        self.u32_type
+    }
+
+    pub fn type_u64(&self) -> Type<'gcc> {
+        self.u64_type
+    }
+
+    pub fn type_u128(&self) -> Type<'gcc> {
+        self.u128_type
+    }
+
+    pub fn type_pointee_for_align(&self, align: Align) -> Type<'gcc> {
+        // FIXME(eddyb) We could find a better approximation if ity.align < align.
+        let ity = Integer::approximate_align(self, align);
+        self.type_from_integer(ity)
+    }
+}
+
+impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn type_i1(&self) -> Type<'gcc> {
+        self.bool_type
+    }
+
+    fn type_i8(&self) -> Type<'gcc> {
+        self.i8_type
+    }
+
+    fn type_i16(&self) -> Type<'gcc> {
+        self.i16_type
+    }
+
+    fn type_i32(&self) -> Type<'gcc> {
+        self.i32_type
+    }
+
+    fn type_i64(&self) -> Type<'gcc> {
+        self.i64_type
+    }
+
+    fn type_i128(&self) -> Type<'gcc> {
+        self.i128_type
+    }
+
+    fn type_isize(&self) -> Type<'gcc> {
+        self.isize_type
+    }
+
+    fn type_f32(&self) -> Type<'gcc> {
+        self.context.new_type::<f32>()
+    }
+
+    fn type_f64(&self) -> Type<'gcc> {
+        self.context.new_type::<f64>()
+    }
+
+    fn type_func(&self, params: &[Type<'gcc>], return_type: Type<'gcc>) -> Type<'gcc> {
+        self.context.new_function_pointer_type(None, return_type, params, false)
+    }
+
+    fn type_struct(&self, fields: &[Type<'gcc>], _packed: bool) -> Type<'gcc> {
+        let types = fields.to_vec();
+        if let Some(typ) = self.struct_types.borrow().get(fields) {
+            return typ.clone();
+        }
+        let fields: Vec<_> = fields.iter().enumerate()
+            .map(|(index, field)| self.context.new_field(None, *field, &format!("field{}_TODO", index)))
+            .collect();
+        // TODO(antoyo): use packed.
+        let typ = self.context.new_struct_type(None, "struct", &fields).as_type();
+        self.struct_types.borrow_mut().insert(types, typ);
+        typ
+    }
+
+    fn type_kind(&self, typ: Type<'gcc>) -> TypeKind {
+        if typ.is_integral() {
+            TypeKind::Integer
+        }
+        else if typ.is_vector().is_some() {
+            TypeKind::Vector
+        }
+        else {
+            // TODO(antoyo): support other types.
+            TypeKind::Void
+        }
+    }
+
+    fn type_ptr_to(&self, ty: Type<'gcc>) -> Type<'gcc> {
+        ty.make_pointer()
+    }
+
+    fn type_ptr_to_ext(&self, ty: Type<'gcc>, _address_space: AddressSpace) -> Type<'gcc> {
+        // TODO(antoyo): use address_space
+        ty.make_pointer()
+    }
+
+    fn element_type(&self, ty: Type<'gcc>) -> Type<'gcc> {
+        if let Some(typ) = ty.is_array() {
+            typ
+        }
+        else if let Some(vector_type) = ty.is_vector() {
+            vector_type.get_element_type()
+        }
+        else if let Some(typ) = ty.get_pointee() {
+            typ
+        }
+        else {
+            unreachable!()
+        }
+    }
+
+    fn vector_length(&self, _ty: Type<'gcc>) -> usize {
+        unimplemented!();
+    }
+
+    fn float_width(&self, typ: Type<'gcc>) -> usize {
+        let f32 = self.context.new_type::<f32>();
+        let f64 = self.context.new_type::<f64>();
+        if typ == f32 {
+            32
+        }
+        else if typ == f64 {
+            64
+        }
+        else {
+            panic!("Cannot get width of float type {:?}", typ);
+        }
+        // TODO(antoyo): support other sizes.
+    }
+
+    fn int_width(&self, typ: Type<'gcc>) -> u64 {
+        if typ.is_i8(self) || typ.is_u8(self) {
+            8
+        }
+        else if typ.is_i16(self) || typ.is_u16(self) {
+            16
+        }
+        else if typ.is_i32(self) || typ.is_u32(self) {
+            32
+        }
+        else if typ.is_i64(self) || typ.is_u64(self) {
+            64
+        }
+        else if typ.is_i128(self) || typ.is_u128(self) {
+            128
+        }
+        else {
+            panic!("Cannot get width of int type {:?}", typ);
+        }
+    }
+
+    fn val_ty(&self, value: RValue<'gcc>) -> Type<'gcc> {
+        value.get_type()
+    }
+}
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+    pub fn type_padding_filler(&self, size: Size, align: Align) -> Type<'gcc> {
+        let unit = Integer::approximate_align(self, align);
+        let size = size.bytes();
+        let unit_size = unit.size().bytes();
+        assert_eq!(size % unit_size, 0);
+        self.type_array(self.type_from_integer(unit), size / unit_size)
+    }
+
+    pub fn set_struct_body(&self, typ: Struct<'gcc>, fields: &[Type<'gcc>], _packed: bool) {
+        // TODO(antoyo): use packed.
+        let fields: Vec<_> = fields.iter().enumerate()
+            .map(|(index, field)| self.context.new_field(None, *field, &format!("field_{}", index)))
+            .collect();
+        typ.set_fields(None, &fields);
+    }
+
+    pub fn type_named_struct(&self, name: &str) -> Struct<'gcc> {
+        self.context.new_opaque_struct_type(None, name)
+    }
+
+    pub fn type_array(&self, ty: Type<'gcc>, mut len: u64) -> Type<'gcc> {
+        if let Some(struct_type) = ty.is_struct() {
+            if struct_type.get_field_count() == 0 {
+                // NOTE: since gccjit only supports i32 for the array size and libcore's tests uses a
+                // size of usize::MAX in test_binary_search, we workaround this by setting the size to
+                // zero for ZSTs.
+                // FIXME(antoyo): fix gccjit API.
+                len = 0;
+            }
+        }
+
+        // NOTE: see note above. Some other test uses usize::MAX.
+        if len == u64::MAX {
+            len = 0;
+        }
+
+        let len: i32 = len.try_into().expect("array len");
+
+        self.context.new_array_type(None, ty, len)
+    }
+}
+
+pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout<'tcx>) -> (Vec<Type<'gcc>>, bool) {
+    let field_count = layout.fields.count();
+
+    let mut packed = false;
+    let mut offset = Size::ZERO;
+    let mut prev_effective_align = layout.align.abi;
+    let mut result: Vec<_> = Vec::with_capacity(1 + field_count * 2);
+    for i in layout.fields.index_by_increasing_offset() {
+        let target_offset = layout.fields.offset(i as usize);
+        let field = layout.field(cx, i);
+        let effective_field_align =
+            layout.align.abi.min(field.align.abi).restrict_for_offset(target_offset);
+        packed |= effective_field_align < field.align.abi;
+
+        assert!(target_offset >= offset);
+        let padding = target_offset - offset;
+        let padding_align = prev_effective_align.min(effective_field_align);
+        assert_eq!(offset.align_to(padding_align) + padding, target_offset);
+        result.push(cx.type_padding_filler(padding, padding_align));
+
+        result.push(field.gcc_type(cx, !field.ty.is_any_ptr())); // FIXME(antoyo): might need to check if the type is inside another, like Box<Type>.
+        offset = target_offset + field.size;
+        prev_effective_align = effective_field_align;
+    }
+    if !layout.is_unsized() && field_count > 0 {
+        if offset > layout.size {
+            bug!("layout: {:#?} stride: {:?} offset: {:?}", layout, layout.size, offset);
+        }
+        let padding = layout.size - offset;
+        let padding_align = prev_effective_align;
+        assert_eq!(offset.align_to(padding_align) + padding, layout.size);
+        result.push(cx.type_padding_filler(padding, padding_align));
+        assert_eq!(result.len(), 1 + field_count * 2);
+    }
+
+    (result, packed)
+}
diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs
new file mode 100644 (file)
index 0000000..9c39c8f
--- /dev/null
@@ -0,0 +1,359 @@
+use std::fmt::Write;
+
+use gccjit::{Struct, Type};
+use crate::rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods};
+use rustc_middle::bug;
+use rustc_middle::ty::{self, Ty, TypeFoldable};
+use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
+use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_target::abi::{self, Abi, F32, F64, FieldsShape, Int, Integer, Pointer, PointeeInfo, Size, TyAbiInterface, Variants};
+use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
+
+use crate::abi::{FnAbiGccExt, GccType};
+use crate::context::CodegenCx;
+use crate::type_::struct_fields;
+
+impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
+    fn type_from_unsigned_integer(&self, i: Integer) -> Type<'gcc> {
+        use Integer::*;
+        match i {
+            I8 => self.type_u8(),
+            I16 => self.type_u16(),
+            I32 => self.type_u32(),
+            I64 => self.type_u64(),
+            I128 => self.type_u128(),
+        }
+    }
+}
+
+pub fn uncached_gcc_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout<'tcx>, defer: &mut Option<(Struct<'gcc>, TyAndLayout<'tcx>)>) -> Type<'gcc> {
+    match layout.abi {
+        Abi::Scalar(_) => bug!("handled elsewhere"),
+        Abi::Vector { ref element, count } => {
+            let element = layout.scalar_gcc_type_at(cx, element, Size::ZERO);
+            return cx.context.new_vector_type(element, count);
+        },
+        Abi::ScalarPair(..) => {
+            return cx.type_struct(
+                &[
+                    layout.scalar_pair_element_gcc_type(cx, 0, false),
+                    layout.scalar_pair_element_gcc_type(cx, 1, false),
+                ],
+                false,
+            );
+        }
+        Abi::Uninhabited | Abi::Aggregate { .. } => {}
+    }
+
+    let name = match layout.ty.kind() {
+        // FIXME(eddyb) producing readable type names for trait objects can result
+        // in problematically distinct types due to HRTB and subtyping (see #47638).
+        // ty::Dynamic(..) |
+        ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Generator(..) | ty::Str
+            if !cx.sess().fewer_names() =>
+        {
+            let mut name = with_no_trimmed_paths(|| layout.ty.to_string());
+            if let (&ty::Adt(def, _), &Variants::Single { index }) =
+                (layout.ty.kind(), &layout.variants)
+            {
+                if def.is_enum() && !def.variants.is_empty() {
+                    write!(&mut name, "::{}", def.variants[index].ident).unwrap();
+                }
+            }
+            if let (&ty::Generator(_, _, _), &Variants::Single { index }) =
+                (layout.ty.kind(), &layout.variants)
+            {
+                write!(&mut name, "::{}", ty::GeneratorSubsts::variant_name(index)).unwrap();
+            }
+            Some(name)
+        }
+        ty::Adt(..) => {
+            // If `Some` is returned then a named struct is created in LLVM. Name collisions are
+            // avoided by LLVM (with increasing suffixes). If rustc doesn't generate names then that
+            // can improve perf.
+            // FIXME(antoyo): I don't think that's true for libgccjit.
+            Some(String::new())
+        }
+        _ => None,
+    };
+
+    match layout.fields {
+        FieldsShape::Primitive | FieldsShape::Union(_) => {
+            let fill = cx.type_padding_filler(layout.size, layout.align.abi);
+            let packed = false;
+            match name {
+                None => cx.type_struct(&[fill], packed),
+                Some(ref name) => {
+                    let gcc_type = cx.type_named_struct(name);
+                    cx.set_struct_body(gcc_type, &[fill], packed);
+                    gcc_type.as_type()
+                },
+            }
+        }
+        FieldsShape::Array { count, .. } => cx.type_array(layout.field(cx, 0).gcc_type(cx, true), count),
+        FieldsShape::Arbitrary { .. } =>
+            match name {
+                None => {
+                    let (gcc_fields, packed) = struct_fields(cx, layout);
+                    cx.type_struct(&gcc_fields, packed)
+                },
+                Some(ref name) => {
+                    let gcc_type = cx.type_named_struct(name);
+                    *defer = Some((gcc_type, layout));
+                    gcc_type.as_type()
+                },
+            },
+    }
+}
+
+pub trait LayoutGccExt<'tcx> {
+    fn is_gcc_immediate(&self) -> bool;
+    fn is_gcc_scalar_pair(&self) -> bool;
+    fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, set_fields: bool) -> Type<'gcc>;
+    fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>;
+    fn scalar_gcc_type_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, scalar: &abi::Scalar, offset: Size) -> Type<'gcc>;
+    fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize, immediate: bool) -> Type<'gcc>;
+    fn gcc_field_index(&self, index: usize) -> u64;
+    fn pointee_info_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, offset: Size) -> Option<PointeeInfo>;
+}
+
+impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
+    fn is_gcc_immediate(&self) -> bool {
+        match self.abi {
+            Abi::Scalar(_) | Abi::Vector { .. } => true,
+            Abi::ScalarPair(..) => false,
+            Abi::Uninhabited | Abi::Aggregate { .. } => self.is_zst(),
+        }
+    }
+
+    fn is_gcc_scalar_pair(&self) -> bool {
+        match self.abi {
+            Abi::ScalarPair(..) => true,
+            Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } | Abi::Aggregate { .. } => false,
+        }
+    }
+
+    /// Gets the GCC type corresponding to a Rust type, i.e., `rustc_middle::ty::Ty`.
+    /// The pointee type of the pointer in `PlaceRef` is always this type.
+    /// For sized types, it is also the right LLVM type for an `alloca`
+    /// containing a value of that type, and most immediates (except `bool`).
+    /// Unsized types, however, are represented by a "minimal unit", e.g.
+    /// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this
+    /// is useful for indexing slices, as `&[T]`'s data pointer is `T*`.
+    /// If the type is an unsized struct, the regular layout is generated,
+    /// with the inner-most trailing unsized field using the "minimal unit"
+    /// of that field's type - this is useful for taking the address of
+    /// that field and ensuring the struct has the right alignment.
+    //TODO(antoyo): do we still need the set_fields parameter?
+    fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, set_fields: bool) -> Type<'gcc> {
+        if let Abi::Scalar(ref scalar) = self.abi {
+            // Use a different cache for scalars because pointers to DSTs
+            // can be either fat or thin (data pointers of fat pointers).
+            if let Some(&ty) = cx.scalar_types.borrow().get(&self.ty) {
+                return ty;
+            }
+            let ty =
+                match *self.ty.kind() {
+                    ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
+                        cx.type_ptr_to(cx.layout_of(ty).gcc_type(cx, set_fields))
+                    }
+                    ty::Adt(def, _) if def.is_box() => {
+                        cx.type_ptr_to(cx.layout_of(self.ty.boxed_ty()).gcc_type(cx, true))
+                    }
+                    ty::FnPtr(sig) => cx.fn_ptr_backend_type(&cx.fn_abi_of_fn_ptr(sig, ty::List::empty())),
+                    _ => self.scalar_gcc_type_at(cx, scalar, Size::ZERO),
+                };
+            cx.scalar_types.borrow_mut().insert(self.ty, ty);
+            return ty;
+        }
+
+        // Check the cache.
+        let variant_index =
+            match self.variants {
+                Variants::Single { index } => Some(index),
+                _ => None,
+            };
+        let cached_type = cx.types.borrow().get(&(self.ty, variant_index)).cloned();
+        if let Some(ty) = cached_type {
+            let type_to_set_fields = cx.types_with_fields_to_set.borrow_mut().remove(&ty);
+            if let Some((struct_type, layout)) = type_to_set_fields {
+                // Since we might be trying to generate a type containing another type which is not
+                // completely generated yet, we deferred setting the fields until now.
+                let (fields, packed) = struct_fields(cx, layout);
+                cx.set_struct_body(struct_type, &fields, packed);
+            }
+            return ty;
+        }
+
+        assert!(!self.ty.has_escaping_bound_vars(), "{:?} has escaping bound vars", self.ty);
+
+        // Make sure lifetimes are erased, to avoid generating distinct LLVM
+        // types for Rust types that only differ in the choice of lifetimes.
+        let normal_ty = cx.tcx.erase_regions(self.ty);
+
+        let mut defer = None;
+        let ty =
+            if self.ty != normal_ty {
+                let mut layout = cx.layout_of(normal_ty);
+                if let Some(v) = variant_index {
+                    layout = layout.for_variant(cx, v);
+                }
+                layout.gcc_type(cx, true)
+            }
+            else {
+                uncached_gcc_type(cx, *self, &mut defer)
+            };
+
+        cx.types.borrow_mut().insert((self.ty, variant_index), ty);
+
+        if let Some((ty, layout)) = defer {
+            let (fields, packed) = struct_fields(cx, layout);
+            cx.set_struct_body(ty, &fields, packed);
+        }
+
+        ty
+    }
+
+    fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> {
+        if let Abi::Scalar(ref scalar) = self.abi {
+            if scalar.is_bool() {
+                return cx.type_i1();
+            }
+        }
+        self.gcc_type(cx, true)
+    }
+
+    fn scalar_gcc_type_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, scalar: &abi::Scalar, offset: Size) -> Type<'gcc> {
+        match scalar.value {
+            Int(i, true) => cx.type_from_integer(i),
+            Int(i, false) => cx.type_from_unsigned_integer(i),
+            F32 => cx.type_f32(),
+            F64 => cx.type_f64(),
+            Pointer => {
+                // If we know the alignment, pick something better than i8.
+                let pointee =
+                    if let Some(pointee) = self.pointee_info_at(cx, offset) {
+                        cx.type_pointee_for_align(pointee.align)
+                    }
+                    else {
+                        cx.type_i8()
+                    };
+                cx.type_ptr_to(pointee)
+            }
+        }
+    }
+
+    fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize, immediate: bool) -> Type<'gcc> {
+        // TODO(antoyo): remove llvm hack:
+        // HACK(eddyb) special-case fat pointers until LLVM removes
+        // pointee types, to avoid bitcasting every `OperandRef::deref`.
+        match self.ty.kind() {
+            ty::Ref(..) | ty::RawPtr(_) => {
+                return self.field(cx, index).gcc_type(cx, true);
+            }
+            ty::Adt(def, _) if def.is_box() => {
+                let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty());
+                return cx.layout_of(ptr_ty).scalar_pair_element_gcc_type(cx, index, immediate);
+            }
+            _ => {}
+        }
+
+        let (a, b) = match self.abi {
+            Abi::ScalarPair(ref a, ref b) => (a, b),
+            _ => bug!("TyAndLayout::scalar_pair_element_llty({:?}): not applicable", self),
+        };
+        let scalar = [a, b][index];
+
+        // Make sure to return the same type `immediate_gcc_type` would when
+        // dealing with an immediate pair.  This means that `(bool, bool)` is
+        // effectively represented as `{i8, i8}` in memory and two `i1`s as an
+        // immediate, just like `bool` is typically `i8` in memory and only `i1`
+        // when immediate.  We need to load/store `bool` as `i8` to avoid
+        // crippling LLVM optimizations or triggering other LLVM bugs with `i1`.
+        // TODO(antoyo): this bugs certainly don't happen in this case since the bool type is used instead of i1.
+        if scalar.is_bool() {
+            return cx.type_i1();
+        }
+
+        let offset =
+            if index == 0 {
+                Size::ZERO
+            }
+            else {
+                a.value.size(cx).align_to(b.value.align(cx).abi)
+            };
+        self.scalar_gcc_type_at(cx, scalar, offset)
+    }
+
+    fn gcc_field_index(&self, index: usize) -> u64 {
+        match self.abi {
+            Abi::Scalar(_) | Abi::ScalarPair(..) => {
+                bug!("TyAndLayout::gcc_field_index({:?}): not applicable", self)
+            }
+            _ => {}
+        }
+        match self.fields {
+            FieldsShape::Primitive | FieldsShape::Union(_) => {
+                bug!("TyAndLayout::gcc_field_index({:?}): not applicable", self)
+            }
+
+            FieldsShape::Array { .. } => index as u64,
+
+            FieldsShape::Arbitrary { .. } => 1 + (self.fields.memory_index(index) as u64) * 2,
+        }
+    }
+
+    fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) -> Option<PointeeInfo> {
+        if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) {
+            return pointee;
+        }
+
+        let result = Ty::ty_and_layout_pointee_info_at(*self, cx, offset);
+
+        cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
+        result
+    }
+}
+
+impl<'gcc, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn backend_type(&self, layout: TyAndLayout<'tcx>) -> Type<'gcc> {
+        layout.gcc_type(self, true)
+    }
+
+    fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> Type<'gcc> {
+        layout.immediate_gcc_type(self)
+    }
+
+    fn is_backend_immediate(&self, layout: TyAndLayout<'tcx>) -> bool {
+        layout.is_gcc_immediate()
+    }
+
+    fn is_backend_scalar_pair(&self, layout: TyAndLayout<'tcx>) -> bool {
+        layout.is_gcc_scalar_pair()
+    }
+
+    fn backend_field_index(&self, layout: TyAndLayout<'tcx>, index: usize) -> u64 {
+        layout.gcc_field_index(index)
+    }
+
+    fn scalar_pair_element_backend_type(&self, layout: TyAndLayout<'tcx>, index: usize, immediate: bool) -> Type<'gcc> {
+        layout.scalar_pair_element_gcc_type(self, index, immediate)
+    }
+
+    fn cast_backend_type(&self, ty: &CastTarget) -> Type<'gcc> {
+        ty.gcc_type(self)
+    }
+
+    fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> {
+        fn_abi.ptr_to_gcc_type(self)
+    }
+
+    fn reg_backend_type(&self, _ty: &Reg) -> Type<'gcc> {
+        unimplemented!();
+    }
+
+    fn fn_decl_backend_type(&self, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> {
+        // FIXME(antoyo): return correct type.
+        self.type_void()
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/test.sh b/compiler/rustc_codegen_gcc/test.sh
new file mode 100755 (executable)
index 0000000..944d0ce
--- /dev/null
@@ -0,0 +1,217 @@
+#!/bin/bash
+
+# TODO(antoyo): rewrite to cargo-make (or just) or something like that to only rebuild the sysroot when needed?
+
+set -e
+
+if [ -f ./gcc_path ]; then 
+    export GCC_PATH=$(cat gcc_path)
+else
+    echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details'
+    exit 1
+fi
+
+export LD_LIBRARY_PATH="$GCC_PATH"
+export LIBRARY_PATH="$GCC_PATH"
+
+if [[ "$1" == "--release" ]]; then
+    export CHANNEL='release'
+    CARGO_INCREMENTAL=1 cargo rustc --release
+    shift
+else
+    echo $LD_LIBRARY_PATH
+    export CHANNEL='debug'
+    cargo rustc
+fi
+
+source config.sh
+
+function clean() {
+    rm -r target/out || true
+    mkdir -p target/out/gccjit
+}
+
+function mini_tests() {
+    echo "[BUILD] mini_core"
+    $RUSTC example/mini_core.rs --crate-name mini_core --crate-type lib,dylib --target $TARGET_TRIPLE
+
+    echo "[BUILD] example"
+    $RUSTC example/example.rs --crate-type lib --target $TARGET_TRIPLE
+
+    echo "[AOT] mini_core_hello_world"
+    $RUSTC example/mini_core_hello_world.rs --crate-name mini_core_hello_world --crate-type bin -g --target $TARGET_TRIPLE
+    $RUN_WRAPPER ./target/out/mini_core_hello_world abc bcd
+}
+
+function build_sysroot() {
+    echo "[BUILD] sysroot"
+    time ./build_sysroot/build_sysroot.sh
+}
+
+function std_tests() {
+    echo "[AOT] arbitrary_self_types_pointers_and_wrappers"
+    $RUSTC example/arbitrary_self_types_pointers_and_wrappers.rs --crate-name arbitrary_self_types_pointers_and_wrappers --crate-type bin --target $TARGET_TRIPLE
+    $RUN_WRAPPER ./target/out/arbitrary_self_types_pointers_and_wrappers
+
+    echo "[AOT] alloc_system"
+    $RUSTC example/alloc_system.rs --crate-type lib --target "$TARGET_TRIPLE"
+
+    echo "[AOT] alloc_example"
+    $RUSTC example/alloc_example.rs --crate-type bin --target $TARGET_TRIPLE
+    $RUN_WRAPPER ./target/out/alloc_example
+
+    echo "[AOT] dst_field_align"
+    # FIXME(antoyo): Re-add -Zmir-opt-level=2 once rust-lang/rust#67529 is fixed.
+    $RUSTC example/dst-field-align.rs --crate-name dst_field_align --crate-type bin --target $TARGET_TRIPLE
+    $RUN_WRAPPER ./target/out/dst_field_align || (echo $?; false)
+
+    echo "[AOT] std_example"
+    $RUSTC example/std_example.rs --crate-type bin --target $TARGET_TRIPLE
+    $RUN_WRAPPER ./target/out/std_example --target $TARGET_TRIPLE
+
+    echo "[AOT] subslice-patterns-const-eval"
+    $RUSTC example/subslice-patterns-const-eval.rs --crate-type bin -Cpanic=abort --target $TARGET_TRIPLE
+    $RUN_WRAPPER ./target/out/subslice-patterns-const-eval
+
+    echo "[AOT] track-caller-attribute"
+    $RUSTC example/track-caller-attribute.rs --crate-type bin -Cpanic=abort --target $TARGET_TRIPLE
+    $RUN_WRAPPER ./target/out/track-caller-attribute
+
+    echo "[BUILD] mod_bench"
+    $RUSTC example/mod_bench.rs --crate-type bin --target $TARGET_TRIPLE
+}
+
+# FIXME(antoyo): linker gives multiple definitions error on Linux
+#echo "[BUILD] sysroot in release mode"
+#./build_sysroot/build_sysroot.sh --release
+
+# TODO(antoyo): uncomment when it works.
+#pushd simple-raytracer
+#if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then
+    #echo "[BENCH COMPILE] ebobby/simple-raytracer"
+    #hyperfine --runs ${RUN_RUNS:-10} --warmup 1 --prepare "rm -r target/*/debug || true" \
+    #"RUSTFLAGS='' cargo build --target $TARGET_TRIPLE" \
+    #"../cargo.sh build"
+
+    #echo "[BENCH RUN] ebobby/simple-raytracer"
+    #cp ./target/*/debug/main ./raytracer_cg_gccjit
+    #hyperfine --runs ${RUN_RUNS:-10} ./raytracer_cg_llvm ./raytracer_cg_gccjit
+#else
+    #echo "[BENCH COMPILE] ebobby/simple-raytracer (skipped)"
+    #echo "[COMPILE] ebobby/simple-raytracer"
+    #../cargo.sh build
+    #echo "[BENCH RUN] ebobby/simple-raytracer (skipped)"
+#fi
+#popd
+
+function test_libcore() {
+    pushd build_sysroot/sysroot_src/library/core/tests
+    echo "[TEST] libcore"
+    rm -r ./target || true
+    ../../../../../cargo.sh test
+    popd
+}
+
+# TODO(antoyo): uncomment when it works.
+#pushd regex
+#echo "[TEST] rust-lang/regex example shootout-regex-dna"
+#../cargo.sh clean
+## Make sure `[codegen mono items] start` doesn't poison the diff
+#../cargo.sh build --example shootout-regex-dna
+#cat examples/regexdna-input.txt | ../cargo.sh run --example shootout-regex-dna | grep -v "Spawned thread" > res.txt
+#diff -u res.txt examples/regexdna-output.txt
+
+#echo "[TEST] rust-lang/regex tests"
+#../cargo.sh test --tests -- --exclude-should-panic --test-threads 1 -Zunstable-options
+#popd
+
+#echo
+#echo "[BENCH COMPILE] mod_bench"
+
+#COMPILE_MOD_BENCH_INLINE="$RUSTC example/mod_bench.rs --crate-type bin -Zmir-opt-level=3 -O --crate-name mod_bench_inline"
+#COMPILE_MOD_BENCH_LLVM_0="rustc example/mod_bench.rs --crate-type bin -Copt-level=0 -o target/out/mod_bench_llvm_0 -Cpanic=abort"
+#COMPILE_MOD_BENCH_LLVM_1="rustc example/mod_bench.rs --crate-type bin -Copt-level=1 -o target/out/mod_bench_llvm_1 -Cpanic=abort"
+#COMPILE_MOD_BENCH_LLVM_2="rustc example/mod_bench.rs --crate-type bin -Copt-level=2 -o target/out/mod_bench_llvm_2 -Cpanic=abort"
+#COMPILE_MOD_BENCH_LLVM_3="rustc example/mod_bench.rs --crate-type bin -Copt-level=3 -o target/out/mod_bench_llvm_3 -Cpanic=abort"
+
+## Use 100 runs, because a single compilations doesn't take more than ~150ms, so it isn't very slow
+#hyperfine --runs ${COMPILE_RUNS:-100} "$COMPILE_MOD_BENCH_INLINE" "$COMPILE_MOD_BENCH_LLVM_0" "$COMPILE_MOD_BENCH_LLVM_1" "$COMPILE_MOD_BENCH_LLVM_2" "$COMPILE_MOD_BENCH_LLVM_3"
+
+#echo
+#echo "[BENCH RUN] mod_bench"
+#hyperfine --runs ${RUN_RUNS:-10} ./target/out/mod_bench{,_inline} ./target/out/mod_bench_llvm_*
+
+function test_rustc() {
+    echo
+    echo "[TEST] rust-lang/rust"
+
+    rust_toolchain=$(cat rust-toolchain)
+
+    git clone https://github.com/rust-lang/rust.git || true
+    cd rust
+    git fetch
+    git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(')
+    export RUSTFLAGS=
+
+    rm config.toml || true
+
+    cat > config.toml <<EOF
+[rust]
+codegen-backends = []
+deny-warnings = false
+
+[build]
+cargo = "$(which cargo)"
+local-rebuild = true
+rustc = "$HOME/.rustup/toolchains/$rust_toolchain-$TARGET_TRIPLE/bin/rustc"
+EOF
+
+    rustc -V | cut -d' ' -f3 | tr -d '('
+    git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(') src/test
+
+    for test in $(rg -i --files-with-matches "//(\[\w+\])?~|// error-pattern:|// build-fail|// run-fail|-Cllvm-args" src/test/ui); do
+      rm $test
+    done
+
+    git checkout -- src/test/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed
+
+    rm -r src/test/ui/{abi*,extern/,llvm-asm/,panic-runtime/,panics/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,simd*,borrowck/,test*,*lto*.rs} || true
+    for test in $(rg --files-with-matches "catch_unwind|should_panic|thread|lto" src/test/ui); do
+      rm $test
+    done
+    git checkout src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs
+    git checkout src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs
+    rm src/test/ui/llvm-asm/llvm-asm-in-out-operand.rs || true # TODO(antoyo): Enable back this test if I ever implement the llvm_asm! macro.
+
+    RUSTC_ARGS="-Zpanic-abort-tests -Zsymbol-mangling-version=v0 -Zcodegen-backend="$(pwd)"/../target/"$CHANNEL"/librustc_codegen_gcc."$dylib_ext" --sysroot "$(pwd)"/../build_sysroot/sysroot -Cpanic=abort"
+
+    echo "[TEST] rustc test suite"
+    COMPILETEST_FORCE_STAGE0=1 ./x.py test --run always --stage 0 src/test/ui/ --rustc-args "$RUSTC_ARGS"
+}
+
+function clean_ui_tests() {
+    find rust/build/x86_64-unknown-linux-gnu/test/ui/ -name stamp -exec rm -rf {} \;
+}
+
+case $1 in
+    "--test-rustc")
+        test_rustc
+        ;;
+
+    "--test-libcore")
+        test_libcore
+        ;;
+
+    "--clean-ui-tests")
+        clean_ui_tests
+        ;;
+
+    *)
+        clean
+        mini_tests
+        build_sysroot
+        std_tests
+        test_libcore
+        test_rustc
+        ;;
+esac
diff --git a/compiler/rustc_codegen_gcc/tests/lib.rs b/compiler/rustc_codegen_gcc/tests/lib.rs
new file mode 100644 (file)
index 0000000..8ee35b3
--- /dev/null
@@ -0,0 +1,50 @@
+use std::{
+    env::{self, current_dir},
+    path::PathBuf,
+    process::Command,
+};
+
+use lang_tester::LangTester;
+use tempfile::TempDir;
+
+fn main() {
+    let tempdir = TempDir::new().expect("temp dir");
+    let current_dir = current_dir().expect("current dir");
+    let current_dir = current_dir.to_str().expect("current dir").to_string();
+    let gcc_path = include_str!("../gcc_path");
+    let gcc_path = gcc_path.trim();
+    env::set_var("LD_LIBRARY_PATH", gcc_path);
+    LangTester::new()
+        .test_dir("tests/run")
+        .test_file_filter(|path| path.extension().expect("extension").to_str().expect("to_str") == "rs")
+        .test_extract(|source| {
+            let lines =
+                source.lines()
+                    .skip_while(|l| !l.starts_with("//"))
+                    .take_while(|l| l.starts_with("//"))
+                    .map(|l| &l[2..])
+                    .collect::<Vec<_>>()
+                    .join("\n");
+            Some(lines)
+        })
+        .test_cmds(move |path| {
+            // Test command 1: Compile `x.rs` into `tempdir/x`.
+            let mut exe = PathBuf::new();
+            exe.push(&tempdir);
+            exe.push(path.file_stem().expect("file_stem"));
+            let mut compiler = Command::new("rustc");
+            compiler.args(&[
+                &format!("-Zcodegen-backend={}/target/debug/librustc_codegen_gcc.so", current_dir),
+                "--sysroot", &format!("{}/build_sysroot/sysroot/", current_dir),
+                "-Zno-parallel-llvm",
+                "-C", "panic=abort",
+                "-C", "link-arg=-lc",
+                "-o", exe.to_str().expect("to_str"),
+                path.to_str().expect("to_str"),
+            ]);
+            // Test command 2: run `tempdir/x`.
+            let runtime = Command::new(exe);
+            vec![("Compiler", compiler), ("Run-time", runtime)]
+        })
+        .run();
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/abort1.rs b/compiler/rustc_codegen_gcc/tests/run/abort1.rs
new file mode 100644 (file)
index 0000000..291af59
--- /dev/null
@@ -0,0 +1,51 @@
+// Compiler:
+//
+// Run-time:
+//   status: signal
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod intrinsics {
+    use super::Sized;
+
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+/*
+ * Code
+ */
+
+fn test_fail() -> ! {
+    unsafe { intrinsics::abort() };
+}
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    test_fail();
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/abort2.rs b/compiler/rustc_codegen_gcc/tests/run/abort2.rs
new file mode 100644 (file)
index 0000000..3c87c56
--- /dev/null
@@ -0,0 +1,53 @@
+// Compiler:
+//
+// Run-time:
+//   status: signal
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod intrinsics {
+    use super::Sized;
+
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+/*
+ * Code
+ */
+
+fn fail() -> i32 {
+    unsafe { intrinsics::abort() };
+    0
+}
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    fail();
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/array.rs b/compiler/rustc_codegen_gcc/tests/run/array.rs
new file mode 100644 (file)
index 0000000..8b621d8
--- /dev/null
@@ -0,0 +1,229 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+//   stdout: 42
+//     7
+//     5
+//     10
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i16 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn printf(format: *const i8, ...) -> i32;
+        pub fn puts(s: *const u8) -> i32;
+    }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+    type Output: ?Sized;
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+impl<T> Index<usize> for [T] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+    // Code here does not matter - this is replaced by the
+    // real drop glue by the compiler.
+    drop_in_place(to_drop);
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+    unsafe {
+        libc::puts("Panicking\0" as *const str as *const u8);
+        intrinsics::abort();
+    }
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+    unsafe {
+        libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+        intrinsics::abort();
+    }
+}
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+    type Output;
+
+    fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i32 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for usize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for isize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+    type Output;
+
+    fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for isize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for u8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i16 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+
+/*
+ * Code
+ */
+
+static mut ONE: usize = 1;
+
+fn make_array() -> [u8; 3] {
+    [42, 10, 5]
+}
+
+#[start]
+fn main(argc: isize, _argv: *const *const u8) -> isize {
+    let array = [42, 7, 5];
+    let array2 = make_array();
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, array[ONE - 1]);
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, array[ONE]);
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, array[ONE + 1]);
+
+        libc::printf(b"%d\n\0" as *const u8 as *const i8, array2[argc as usize] as u32);
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/asm.rs b/compiler/rustc_codegen_gcc/tests/run/asm.rs
new file mode 100644 (file)
index 0000000..9c0055b
--- /dev/null
@@ -0,0 +1,153 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+
+#![feature(asm, global_asm)]
+
+global_asm!("
+    .global add_asm
+add_asm:
+     mov rax, rdi
+     add rax, rsi
+     ret"
+);
+
+extern "C" {
+    fn add_asm(a: i64, b: i64) -> i64;
+}
+
+fn main() {
+    unsafe {
+        asm!("nop");
+    }
+
+    let x: u64;
+    unsafe {
+        asm!("mov $5, {}",
+            out(reg) x,
+            options(att_syntax)
+        );
+    }
+    assert_eq!(x, 5);
+
+    let x: u64;
+    let input: u64 = 42;
+    unsafe {
+        asm!("mov {input}, {output}",
+             "add $1, {output}",
+            input = in(reg) input,
+            output = out(reg) x,
+            options(att_syntax)
+        );
+    }
+    assert_eq!(x, 43);
+
+    let x: u64;
+    unsafe {
+        asm!("mov {}, 6",
+            out(reg) x,
+        );
+    }
+    assert_eq!(x, 6);
+
+    let x: u64;
+    let input: u64 = 42;
+    unsafe {
+        asm!("mov {output}, {input}",
+             "add {output}, 1",
+            input = in(reg) input,
+            output = out(reg) x,
+        );
+    }
+    assert_eq!(x, 43);
+
+    // check inout(reg_class) x 
+    let mut x: u64 = 42;
+    unsafe {
+        asm!("add {0}, {0}",
+            inout(reg) x 
+        );
+    }
+    assert_eq!(x, 84);
+
+    // check inout("reg") x
+    let mut x: u64 = 42;
+    unsafe {
+        asm!("add r11, r11",
+            inout("r11") x 
+        );
+    }
+    assert_eq!(x, 84);
+
+    // check a mix of
+    // in("reg")
+    // inout(class) x => y
+    // inout (class) x
+    let x: u64 = 702;
+    let y: u64 = 100;
+    let res: u64;
+    let mut rem: u64 = 0;
+    unsafe {
+        asm!("div r11",
+            in("r11") y,
+            inout("eax") x => res,
+            inout("edx") rem,
+        );
+    }
+    assert_eq!(res, 7);
+    assert_eq!(rem, 2);
+
+    // check const 
+    let mut x: u64 = 42;
+    unsafe {
+        asm!("add {}, {}",
+            inout(reg) x,
+            const 1 
+        );
+    }
+    assert_eq!(x, 43);
+
+    // check const (ATT syntax)
+    let mut x: u64 = 42;
+    unsafe {
+        asm!("add {}, {}",
+            const 1,
+            inout(reg) x,
+            options(att_syntax)
+        );
+    }
+    assert_eq!(x, 43);
+
+    // check sym fn
+    extern "C" fn foo() -> u64 { 42 }
+    let x: u64;
+    unsafe {
+        asm!("call {}", sym foo, lateout("rax") x);
+    }
+    assert_eq!(x, 42);
+
+    // check sym fn (ATT syntax)
+    let x: u64;
+    unsafe {
+        asm!("call {}", sym foo, lateout("rax") x, options(att_syntax));
+    }
+    assert_eq!(x, 42);
+
+    // check sym static
+    static FOO: u64 = 42;
+    let x: u64;
+    unsafe {
+        asm!("mov {1}, qword ptr [rip + {0}]", sym FOO, lateout(reg) x);
+    }
+    assert_eq!(x, 42);
+
+    // check sym static (ATT syntax)
+    let x: u64;
+    unsafe {
+        asm!("movq {0}(%rip), {1}", sym FOO, lateout(reg) x, options(att_syntax));
+    }
+    assert_eq!(x, 42);
+
+    assert_eq!(unsafe { add_asm(40, 2) }, 42);
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/assign.rs b/compiler/rustc_codegen_gcc/tests/run/assign.rs
new file mode 100644 (file)
index 0000000..cc86470
--- /dev/null
@@ -0,0 +1,153 @@
+// Compiler:
+//
+// Run-time:
+//   stdout: 2
+//     7 8
+//     10
+
+#![allow(unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, track_caller)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for *mut i32 {}
+impl Copy for usize {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i32 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn puts(s: *const u8) -> i32;
+        pub fn fflush(stream: *mut i32) -> i32;
+        pub fn printf(format: *const i8, ...) -> i32;
+
+        pub static STDOUT: *mut i32;
+    }
+}
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+    unsafe {
+        libc::puts("Panicking\0" as *const str as *const u8);
+        libc::fflush(libc::STDOUT);
+        intrinsics::abort();
+    }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+    type Output;
+
+    fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i32 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for usize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for isize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+/*
+ * Code
+ */
+
+fn inc_ref(num: &mut isize) -> isize {
+    *num = *num + 5;
+    *num + 1
+}
+
+fn inc(num: isize) -> isize {
+    num + 1
+}
+
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    argc = inc(argc);
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc);
+    }
+
+    let b = inc_ref(&mut argc);
+    unsafe {
+        libc::printf(b"%ld %ld\n\0" as *const u8 as *const i8, argc, b);
+    }
+
+    argc = 10;
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc);
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/closure.rs b/compiler/rustc_codegen_gcc/tests/run/closure.rs
new file mode 100644 (file)
index 0000000..7121a5f
--- /dev/null
@@ -0,0 +1,230 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+//   stdout: Arg: 1
+//     Argument: 1
+//     String arg: 1
+//     Int argument: 2
+//     Both args: 11
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics,
+    unboxed_closures)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u32 {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn puts(s: *const u8) -> i32;
+        pub fn printf(format: *const i8, ...) -> i32;
+    }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+    type Output: ?Sized;
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+impl<T> Index<usize> for [T] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+    // Code here does not matter - this is replaced by the
+    // real drop glue by the compiler.
+    drop_in_place(to_drop);
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+    unsafe {
+        libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+        intrinsics::abort();
+    }
+}
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+#[lang = "unsize"]
+pub trait Unsize<T: ?Sized> {}
+
+#[lang = "coerce_unsized"]
+pub trait CoerceUnsized<T> {}
+
+impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+
+#[lang = "fn_once"]
+#[rustc_paren_sugar]
+pub trait FnOnce<Args> {
+    #[lang = "fn_once_output"]
+    type Output;
+
+    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+}
+
+#[lang = "fn_mut"]
+#[rustc_paren_sugar]
+pub trait FnMut<Args>: FnOnce<Args> {
+    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+    type Output;
+
+    fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i32 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for usize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for isize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+    unsafe {
+        libc::puts("Panicking\0" as *const str as *const u8);
+        intrinsics::abort();
+    }
+}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    let string = "Arg: %d\n\0";
+    let mut closure = || {
+        unsafe {
+            libc::printf(string as *const str as *const i8, argc);
+        }
+    };
+    closure();
+
+    let mut closure = || {
+        unsafe {
+            libc::printf("Argument: %d\n\0" as *const str as *const i8, argc);
+        }
+    };
+    closure();
+
+    let mut closure = |string| {
+        unsafe {
+            libc::printf(string as *const str as *const i8, argc);
+        }
+    };
+    closure("String arg: %d\n\0");
+
+    let mut closure = |arg: isize| {
+        unsafe {
+            libc::printf("Int argument: %d\n\0" as *const str as *const i8, arg);
+        }
+    };
+    closure(argc + 1);
+
+    let mut closure = |string, arg: isize| {
+        unsafe {
+            libc::printf(string as *const str as *const i8, arg);
+        }
+    };
+    closure("Both args: %d\n\0", argc + 10);
+
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/condition.rs b/compiler/rustc_codegen_gcc/tests/run/condition.rs
new file mode 100644 (file)
index 0000000..6a2e2d5
--- /dev/null
@@ -0,0 +1,320 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+//   stdout: true
+//     1
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for u64 {}
+impl Copy for i32 {}
+impl Copy for u32 {}
+impl Copy for bool {}
+impl Copy for u16 {}
+impl Copy for i16 {}
+impl Copy for char {}
+impl Copy for i8 {}
+impl Copy for u8 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn printf(format: *const i8, ...) -> i32;
+        pub fn puts(s: *const u8) -> i32;
+    }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+    type Output: ?Sized;
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+impl<T> Index<usize> for [T] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+    // Code here does not matter - this is replaced by the
+    // real drop glue by the compiler.
+    drop_in_place(to_drop);
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+    unsafe {
+        libc::puts("Panicking\0" as *const str as *const u8);
+        intrinsics::abort();
+    }
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+    unsafe {
+        libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+        intrinsics::abort();
+    }
+}
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+    type Output;
+
+    fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i32 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for usize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for isize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+    type Output;
+
+    fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for isize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for u8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i16 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+#[lang = "eq"]
+pub trait PartialEq<Rhs: ?Sized = Self> {
+    fn eq(&self, other: &Rhs) -> bool;
+    fn ne(&self, other: &Rhs) -> bool;
+}
+
+impl PartialEq for u8 {
+    fn eq(&self, other: &u8) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &u8) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for u16 {
+    fn eq(&self, other: &u16) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &u16) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for u32 {
+    fn eq(&self, other: &u32) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &u32) -> bool {
+        (*self) != (*other)
+    }
+}
+
+
+impl PartialEq for u64 {
+    fn eq(&self, other: &u64) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &u64) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for usize {
+    fn eq(&self, other: &usize) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &usize) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for i8 {
+    fn eq(&self, other: &i8) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &i8) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for i32 {
+    fn eq(&self, other: &i32) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &i32) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for isize {
+    fn eq(&self, other: &isize) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &isize) -> bool {
+        (*self) != (*other)
+    }
+}
+
+impl PartialEq for char {
+    fn eq(&self, other: &char) -> bool {
+        (*self) == (*other)
+    }
+    fn ne(&self, other: &char) -> bool {
+        (*self) != (*other)
+    }
+}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        if argc == 1 {
+            libc::printf(b"true\n\0" as *const u8 as *const i8);
+        }
+
+        let string =
+            match argc {
+                1 => b"1\n\0",
+                2 => b"2\n\0",
+                3 => b"3\n\0",
+                4 => b"4\n\0",
+                5 => b"5\n\0",
+                _ => b"_\n\0",
+            };
+        libc::printf(string as *const u8 as *const i8);
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs
new file mode 100644 (file)
index 0000000..c02cfd2
--- /dev/null
@@ -0,0 +1,39 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+
+#![feature(auto_traits, lang_items, no_core, start)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/exit.rs b/compiler/rustc_codegen_gcc/tests/run/exit.rs
new file mode 100644 (file)
index 0000000..956e53d
--- /dev/null
@@ -0,0 +1,49 @@
+// Compiler:
+//
+// Run-time:
+//   status: 2
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn exit(status: i32);
+    }
+}
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        libc::exit(2);
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs
new file mode 100644 (file)
index 0000000..eeab352
--- /dev/null
@@ -0,0 +1,39 @@
+// Compiler:
+//
+// Run-time:
+//   status: 1
+
+#![feature(auto_traits, lang_items, no_core, start)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    1
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs
new file mode 100644 (file)
index 0000000..a226fff
--- /dev/null
@@ -0,0 +1,223 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+//   stdout: 1
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i16 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn printf(format: *const i8, ...) -> i32;
+        pub fn puts(s: *const u8) -> i32;
+    }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+    type Output: ?Sized;
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+impl<T> Index<usize> for [T] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+    // Code here does not matter - this is replaced by the
+    // real drop glue by the compiler.
+    drop_in_place(to_drop);
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+    unsafe {
+        libc::puts("Panicking\0" as *const str as *const u8);
+        intrinsics::abort();
+    }
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+    unsafe {
+        libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+        intrinsics::abort();
+    }
+}
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+    type Output;
+
+    fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i32 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for usize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for isize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+    type Output;
+
+    fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for isize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for u8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i16 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+
+/*
+ * Code
+ */
+
+fn i16_as_i8(a: i16) -> i8 {
+    a as i8
+}
+
+fn call_func(func: fn(i16) -> i8, param: i16) -> i8 {
+    func(param)
+}
+
+#[start]
+fn main(argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        let result = call_func(i16_as_i8, argc as i16) as isize;
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, result);
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/int_overflow.rs b/compiler/rustc_codegen_gcc/tests/run/int_overflow.rs
new file mode 100644 (file)
index 0000000..7111703
--- /dev/null
@@ -0,0 +1,129 @@
+// Compiler:
+//
+// Run-time:
+//   stdout: Panicking
+//   status: signal
+
+#![allow(unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for *mut i32 {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn puts(s: *const u8) -> i32;
+        pub fn fflush(stream: *mut i32) -> i32;
+
+        pub static STDOUT: *mut i32;
+    }
+}
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+    unsafe {
+        libc::puts("Panicking\0" as *const str as *const u8);
+        libc::fflush(libc::STDOUT);
+        intrinsics::abort();
+    }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+    type Output;
+
+    fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i32 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for usize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for isize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    let int = 9223372036854775807isize;
+    let int = int + argc;
+    int
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs
new file mode 100644 (file)
index 0000000..e887600
--- /dev/null
@@ -0,0 +1,165 @@
+
+// Compiler:
+//
+// Run-time:
+//   stdout: 2
+//     7
+//     6
+//     11
+
+#![allow(unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, track_caller)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for *mut i32 {}
+impl Copy for usize {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i32 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn puts(s: *const u8) -> i32;
+        pub fn fflush(stream: *mut i32) -> i32;
+        pub fn printf(format: *const i8, ...) -> i32;
+
+        pub static STDOUT: *mut i32;
+    }
+}
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+    unsafe {
+        libc::puts("Panicking\0" as *const str as *const u8);
+        libc::fflush(libc::STDOUT);
+        intrinsics::abort();
+    }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+    type Output;
+
+    fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i32 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for usize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for isize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+/*
+ * Code
+ */
+
+struct Test {
+    field: isize,
+}
+
+fn test(num: isize) -> Test {
+    Test {
+        field: num + 1,
+    }
+}
+
+fn update_num(num: &mut isize) {
+    *num = *num + 5;
+}
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    let mut test = test(argc);
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field);
+    }
+    update_num(&mut test.field);
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field);
+    }
+
+    update_num(&mut argc);
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc);
+    }
+
+    let refe = &mut argc;
+    *refe = *refe + 5;
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc);
+    }
+
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/operations.rs b/compiler/rustc_codegen_gcc/tests/run/operations.rs
new file mode 100644 (file)
index 0000000..4dc3753
--- /dev/null
@@ -0,0 +1,221 @@
+// Compiler:
+//
+// Run-time:
+//   stdout: 41
+//     39
+//     10
+
+#![allow(unused_attributes)]
+#![feature(auto_traits, lang_items, no_core, start, intrinsics, arbitrary_self_types)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for *mut i32 {}
+impl Copy for usize {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i16 {}
+impl Copy for i32 {}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target: ?Sized;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn printf(format: *const i8, ...) -> i32;
+        pub fn puts(s: *const u8) -> i32;
+        pub fn fflush(stream: *mut i32) -> i32;
+
+        pub static STDOUT: *mut i32;
+    }
+}
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+    unsafe {
+        libc::puts("Panicking\0" as *const str as *const u8);
+        libc::fflush(libc::STDOUT);
+        intrinsics::abort();
+    }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+    type Output;
+
+    fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i32 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for usize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for isize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+    type Output;
+
+    fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for isize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for u8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i16 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+#[lang = "mul"]
+pub trait Mul<RHS = Self> {
+    type Output;
+
+    #[must_use]
+    fn mul(self, rhs: RHS) -> Self::Output;
+}
+
+impl Mul for u8 {
+    type Output = Self;
+
+    fn mul(self, rhs: Self) -> Self::Output {
+        self * rhs
+    }
+}
+
+impl Mul for usize {
+    type Output = Self;
+
+    fn mul(self, rhs: Self) -> Self::Output {
+        self * rhs
+    }
+}
+
+impl Mul for isize {
+    type Output = Self;
+
+    fn mul(self, rhs: Self) -> Self::Output {
+        self * rhs
+    }
+}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, 40 + argc);
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, 40 - argc);
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, 10 * argc);
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs
new file mode 100644 (file)
index 0000000..6ac099e
--- /dev/null
@@ -0,0 +1,222 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+//   stdout: 1
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u8 {}
+impl Copy for i8 {}
+impl Copy for i16 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn printf(format: *const i8, ...) -> i32;
+        pub fn puts(s: *const u8) -> i32;
+    }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+    type Output: ?Sized;
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+impl<T> Index<usize> for [T] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+    // Code here does not matter - this is replaced by the
+    // real drop glue by the compiler.
+    drop_in_place(to_drop);
+}
+
+#[lang = "panic"]
+#[track_caller]
+#[no_mangle]
+pub fn panic(_msg: &str) -> ! {
+    unsafe {
+        libc::puts("Panicking\0" as *const str as *const u8);
+        intrinsics::abort();
+    }
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+    unsafe {
+        libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+        intrinsics::abort();
+    }
+}
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+#[lang = "add"]
+trait Add<RHS = Self> {
+    type Output;
+
+    fn add(self, rhs: RHS) -> Self::Output;
+}
+
+impl Add for u8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i8 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for i32 {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for usize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+impl Add for isize {
+    type Output = Self;
+
+    fn add(self, rhs: Self) -> Self {
+        self + rhs
+    }
+}
+
+#[lang = "sub"]
+pub trait Sub<RHS = Self> {
+    type Output;
+
+    fn sub(self, rhs: RHS) -> Self::Output;
+}
+
+impl Sub for usize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for isize {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for u8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i8 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+impl Sub for i16 {
+    type Output = Self;
+
+    fn sub(self, rhs: Self) -> Self {
+        self - rhs
+    }
+}
+
+
+/*
+ * Code
+ */
+
+static mut ONE: usize = 1;
+
+fn make_array() -> [u8; 3] {
+    [42, 10, 5]
+}
+
+#[start]
+fn main(argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        let ptr = ONE as *mut usize;
+        let value = ptr as usize;
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, value);
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs
new file mode 100644 (file)
index 0000000..6fa10dc
--- /dev/null
@@ -0,0 +1,72 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+//   stdout: 10
+//     10
+//     42
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+#[lang = "copy"]
+pub unsafe trait Copy {}
+
+unsafe impl Copy for bool {}
+unsafe impl Copy for u8 {}
+unsafe impl Copy for u16 {}
+unsafe impl Copy for u32 {}
+unsafe impl Copy for u64 {}
+unsafe impl Copy for usize {}
+unsafe impl Copy for i8 {}
+unsafe impl Copy for i16 {}
+unsafe impl Copy for i32 {}
+unsafe impl Copy for isize {}
+unsafe impl Copy for f32 {}
+unsafe impl Copy for char {}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn printf(format: *const i8, ...) -> i32;
+    }
+}
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+/*
+ * Code
+ */
+
+fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u32) {
+    (
+        a as u8, a as u16, a as u32, a as usize, a as i8, a as i16, a as i32, a as isize, b as u8,
+        b as u32,
+    )
+}
+
+#[start]
+fn main(argc: isize, _argv: *const *const u8) -> isize {
+    let (a, b, c, d, e, f, g, h, i, j) = int_cast(10, 42);
+    unsafe {
+        libc::printf(b"%d\n\0" as *const u8 as *const i8, c);
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, d);
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, j);
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/slice.rs b/compiler/rustc_codegen_gcc/tests/run/slice.rs
new file mode 100644 (file)
index 0000000..ad9258e
--- /dev/null
@@ -0,0 +1,128 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+//   stdout: 5
+
+#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+impl Copy for usize {}
+impl Copy for i32 {}
+impl Copy for u32 {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn printf(format: *const i8, ...) -> i32;
+    }
+}
+
+#[lang = "index"]
+pub trait Index<Idx: ?Sized> {
+    type Output: ?Sized;
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> Index<usize> for [T; 3] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+impl<T> Index<usize> for [T] {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self[index]
+    }
+}
+
+#[lang = "unsize"]
+pub trait Unsize<T: ?Sized> {}
+
+#[lang = "coerce_unsized"]
+pub trait CoerceUnsized<T> {}
+
+impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+    // Code here does not matter - this is replaced by the
+    // real drop glue by the compiler.
+    drop_in_place(to_drop);
+}
+
+#[lang = "panic_location"]
+struct PanicLocation {
+    file: &'static str,
+    line: u32,
+    column: u32,
+}
+
+#[lang = "panic_bounds_check"]
+#[track_caller]
+#[no_mangle]
+fn panic_bounds_check(index: usize, len: usize) -> ! {
+    unsafe {
+        libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
+        intrinsics::abort();
+    }
+}
+
+mod intrinsics {
+    use super::Sized;
+
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+/*
+ * Code
+ */
+
+static mut TWO: usize = 2;
+
+fn index_slice(s: &[u32]) -> u32 {
+    unsafe {
+        s[TWO]
+    }
+}
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    let array = [42, 7, 5];
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, index_slice(&array));
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/static.rs b/compiler/rustc_codegen_gcc/tests/run/static.rs
new file mode 100644 (file)
index 0000000..ab89f6a
--- /dev/null
@@ -0,0 +1,106 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+//   stdout: 10
+//      14
+//      1
+//      12
+//      12
+//      1
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod intrinsics {
+    use super::Sized;
+
+    extern "rust-intrinsic" {
+        pub fn abort() -> !;
+    }
+}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn printf(format: *const i8, ...) -> i32;
+    }
+}
+
+#[lang = "structural_peq"]
+pub trait StructuralPartialEq {}
+
+#[lang = "structural_teq"]
+pub trait StructuralEq {}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+    // Code here does not matter - this is replaced by the
+    // real drop glue by the compiler.
+    drop_in_place(to_drop);
+}
+
+/*
+ * Code
+ */
+
+struct Test {
+    field: isize,
+}
+
+struct WithRef {
+    refe: &'static Test,
+}
+
+static mut CONSTANT: isize = 10;
+
+static mut TEST: Test = Test {
+    field: 12,
+};
+
+static mut TEST2: Test = Test {
+    field: 14,
+};
+
+static mut WITH_REF: WithRef = WithRef {
+    refe: unsafe { &TEST },
+};
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, CONSTANT);
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST2.field);
+        TEST2.field = argc;
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST2.field);
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, WITH_REF.refe.field);
+        WITH_REF.refe = &TEST2;
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST.field);
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, WITH_REF.refe.field);
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/structs.rs b/compiler/rustc_codegen_gcc/tests/run/structs.rs
new file mode 100644 (file)
index 0000000..6c88848
--- /dev/null
@@ -0,0 +1,70 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+//   stdout: 1
+//     2
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn printf(format: *const i8, ...) -> i32;
+    }
+}
+
+/*
+ * Code
+ */
+
+struct Test {
+    field: isize,
+}
+
+struct Two {
+    two: isize,
+}
+
+fn one() -> isize {
+    1
+}
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    let test = Test {
+        field: one(),
+    };
+    let two = Two {
+        two: 2,
+    };
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field);
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, two.two);
+    }
+    0
+}
diff --git a/compiler/rustc_codegen_gcc/tests/run/tuple.rs b/compiler/rustc_codegen_gcc/tests/run/tuple.rs
new file mode 100644 (file)
index 0000000..0b670bf
--- /dev/null
@@ -0,0 +1,51 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+//   stdout: 3
+
+#![feature(auto_traits, lang_items, no_core, start, intrinsics)]
+
+#![no_std]
+#![no_core]
+
+/*
+ * Core
+ */
+
+// Because we don't have core yet.
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "copy"]
+trait Copy {
+}
+
+impl Copy for isize {}
+
+#[lang = "receiver"]
+trait Receiver {
+}
+
+#[lang = "freeze"]
+pub(crate) unsafe auto trait Freeze {}
+
+mod libc {
+    #[link(name = "c")]
+    extern "C" {
+        pub fn printf(format: *const i8, ...) -> i32;
+    }
+}
+
+/*
+ * Code
+ */
+
+#[start]
+fn main(mut argc: isize, _argv: *const *const u8) -> isize {
+    let test: (isize, isize, isize) = (3, 1, 4);
+    unsafe {
+        libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.0);
+    }
+    0
+}
index a6bdbd11899deab8bb81cba65f3b8a7889d13bea..3026c2fa0309aed4c046e3dc3da594fba0927672 100644 (file)
@@ -25,9 +25,9 @@
 use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::{ModuleCodegen, ModuleKind};
 use rustc_data_structures::small_c_str::SmallCStr;
+use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
-use rustc_middle::middle::cstore::EncodedMetadata;
 use rustc_middle::middle::exported_symbols;
 use rustc_middle::mir::mono::{Linkage, Visibility};
 use rustc_middle::ty::TyCtxt;
@@ -64,7 +64,7 @@ pub fn write_compressed_metadata<'tcx>(
 
     let (metadata_llcx, metadata_llmod) = (&*llvm_module.llcx, llvm_module.llmod());
     let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
-    FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap();
+    FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
 
     let llmeta = common::bytes_in_context(metadata_llcx, &compressed);
     let llconst = common::struct_in_context(metadata_llcx, &[llmeta], false);
index 1da14344b1d26616a6f8f4a2f7d42ef28824cfe9..79bdace5158326833ae28e073e359c61d3876ee8 100644 (file)
@@ -27,8 +27,8 @@
 use rustc_codegen_ssa::{CodegenResults, CompiledModule};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{ErrorReported, FatalError, Handler};
+use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::EncodedMetadata;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::{OptLevel, OutputFilenames, PrintRequest};
 use rustc_session::Session;
index 0713e167c53b8b73a7962f0bd2100e13c60444c3..54641df61793e37c120920d7b779674cd3b9e2d4 100644 (file)
@@ -32,6 +32,7 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_incremental = { path = "../rustc_incremental" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
+rustc_metadata = { path = "../rustc_metadata" }
 rustc_target = { path = "../rustc_target" }
 rustc_session = { path = "../rustc_session" }
 
index 826c09cd948f649e0edf10d9cf26b6e835b06ae9..1c0442a231a580e7607b7846b97069227cb4eb50 100644 (file)
@@ -327,7 +327,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
             // metadata in rlib files is wrapped in a "dummy" object file for
             // the target platform so the rlib can be processed entirely by
             // normal linkers for the platform.
-            let metadata = create_metadata_file(sess, &codegen_results.metadata.raw_data);
+            let metadata = create_metadata_file(sess, codegen_results.metadata.raw_data());
             ab.add_file(&emit_metadata(sess, &metadata, tmpdir));
 
             // After adding all files to the archive, we need to update the
index 41823f7d80d69acd3d28c95060a91c4b517c33b4..31722d07414d062203d09abb33860ec12e8513a1 100644 (file)
@@ -21,8 +21,8 @@
 use rustc_incremental::{
     copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess,
 };
+use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::EncodedMetadata;
 use rustc_middle::middle::exported_symbols::SymbolExportLevel;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::cgu_reuse_tracker::CguReuseTracker;
index a6bf1d8d1e51a2ef596dc6e2c4532b13a75df1ad..9bb4982754c20cb3acaecd78c991603d540e6bc3 100644 (file)
@@ -18,8 +18,8 @@
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::lang_items::LangItem;
 use rustc_index::vec::Idx;
+use rustc_metadata::EncodedMetadata;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
-use rustc_middle::middle::cstore::EncodedMetadata;
 use rustc_middle::middle::lang_items;
 use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
index 634286770d1f92ccb45a27cfc815d02dcff298e2..70b351f643314c24fbe25f9417962bd1424f4983 100644 (file)
@@ -158,7 +158,7 @@ pub struct CodegenResults {
     pub modules: Vec<CompiledModule>,
     pub allocator_module: Option<CompiledModule>,
     pub metadata_module: Option<CompiledModule>,
-    pub metadata: rustc_middle::middle::cstore::EncodedMetadata,
+    pub metadata: rustc_metadata::EncodedMetadata,
     pub crate_info: CrateInfo,
 }
 
index 82b79fd0b2ac3e1d525d2ec622a8b8332a081639..50a46877c5acaeb5bafe93917bd2e90b3a405fbc 100644 (file)
@@ -6,8 +6,9 @@
 use rustc_ast::expand::allocator::AllocatorKind;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorReported;
+use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn};
+use rustc_middle::middle::cstore::MetadataLoaderDyn;
 use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf, TyAndLayout};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{Ty, TyCtxt};
index bcce19b28db97444e189f25833a497dd45d9ae96..e6037d561dedcb5228ed84e93e524df136f17551 100644 (file)
@@ -197,12 +197,17 @@ pub fn eval_rvalue_into_place(
             }
 
             Aggregate(ref kind, ref operands) => {
+                // active_field_index is for union initialization.
                 let (dest, active_field_index) = match **kind {
                     mir::AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
                         self.write_discriminant(variant_index, &dest)?;
                         if adt_def.is_enum() {
-                            (self.place_downcast(&dest, variant_index)?, active_field_index)
+                            assert!(active_field_index.is_none());
+                            (self.place_downcast(&dest, variant_index)?, None)
                         } else {
+                            if active_field_index.is_some() {
+                                assert_eq!(operands.len(), 1);
+                            }
                             (dest, active_field_index)
                         }
                     }
@@ -211,12 +216,9 @@ pub fn eval_rvalue_into_place(
 
                 for (i, operand) in operands.iter().enumerate() {
                     let op = self.eval_operand(operand, None)?;
-                    // Ignore zero-sized fields.
-                    if !op.layout.is_zst() {
-                        let field_index = active_field_index.unwrap_or(i);
-                        let field_dest = self.place_field(&dest, field_index)?;
-                        self.copy_op(&op, &field_dest)?;
-                    }
+                    let field_index = active_field_index.unwrap_or(i);
+                    let field_dest = self.place_field(&dest, field_index)?;
+                    self.copy_op(&op, &field_dest)?;
                 }
             }
 
@@ -253,7 +255,6 @@ pub fn eval_rvalue_into_place(
             }
 
             Len(place) => {
-                // FIXME(CTFE): don't allow computing the length of arrays in const eval
                 let src = self.eval_place(place)?;
                 let mplace = self.force_allocation(&src)?;
                 let len = mplace.len(self)?;
index d8ac815a15821c114860dca527a26e9fdd62c87e..b84f28b6a9edf408999fd4dde15a3da2e90b0ef3 100644 (file)
@@ -405,6 +405,7 @@ fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, S> {
     /// Call this method when `inspect_node` has returned `None`. Having the
     /// caller decide avoids mutual recursion between the two methods and allows
     /// us to maintain an allocated stack for nodes on the path between calls.
+    #[instrument(skip(self, initial), level = "debug")]
     fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<S> {
         struct VisitingNodeFrame<G: DirectedGraph, Successors> {
             node: G::Node,
@@ -451,7 +452,7 @@ struct VisitingNodeFrame<G: DirectedGraph, Successors> {
                 Some(iter) => iter,
                 None => {
                     // This None marks that we still have the initialize this node's frame.
-                    debug!("walk_unvisited_node(depth = {:?}, node = {:?})", depth, node);
+                    debug!(?depth, ?node);
 
                     debug_assert!(matches!(self.node_states[node], NodeState::NotVisited));
 
@@ -478,10 +479,7 @@ struct VisitingNodeFrame<G: DirectedGraph, Successors> {
                 return_value.take().into_iter().map(|walk| (*successor_node, Some(walk)));
 
             let successor_walk = successors.by_ref().map(|successor_node| {
-                debug!(
-                    "walk_unvisited_node: node = {:?} successor_ode = {:?}",
-                    node, successor_node
-                );
+                debug!(?node, ?successor_node);
                 (successor_node, self.inspect_node(successor_node))
             });
 
@@ -491,10 +489,7 @@ struct VisitingNodeFrame<G: DirectedGraph, Successors> {
                         // Track the minimum depth we can reach.
                         assert!(successor_min_depth <= depth);
                         if successor_min_depth < *min_depth {
-                            debug!(
-                                "walk_unvisited_node: node = {:?} successor_min_depth = {:?}",
-                                node, successor_min_depth
-                            );
+                            debug!(?node, ?successor_min_depth);
                             *min_depth = successor_min_depth;
                             *min_cycle_root = successor_node;
                         }
@@ -503,16 +498,13 @@ struct VisitingNodeFrame<G: DirectedGraph, Successors> {
                     Some(WalkReturn::Complete { scc_index: successor_scc_index }) => {
                         // Push the completed SCC indices onto
                         // the `successors_stack` for later.
-                        debug!(
-                            "walk_unvisited_node: node = {:?} successor_scc_index = {:?}",
-                            node, successor_scc_index
-                        );
+                        debug!(?node, ?successor_scc_index);
                         successors_stack.push(successor_scc_index);
                     }
 
                     None => {
                         let depth = depth + 1;
-                        debug!("walk_node(depth = {:?}, node = {:?})", depth, successor_node);
+                        debug!(?depth, ?successor_node);
                         // Remember which node the return value will come from.
                         frame.successor_node = successor_node;
                         // Start a new stack frame the step into it.
index d32593f34adefc750c68f8a9ed4f1b20f5f8a29c..1d6703077acffadd22629e638e6bd9256f3e5cab 100644 (file)
@@ -633,14 +633,18 @@ fn collect_invocations(
 
     fn error_recursion_limit_reached(&mut self) {
         let expn_data = self.cx.current_expansion.id.expn_data();
-        let suggested_limit = self.cx.ecfg.recursion_limit * 2;
+        let suggested_limit = match self.cx.ecfg.recursion_limit {
+            Limit(0) => Limit(2),
+            limit => limit * 2,
+        };
         self.cx
             .struct_span_err(
                 expn_data.call_site,
                 &format!("recursion limit reached while expanding `{}`", expn_data.kind.descr()),
             )
             .help(&format!(
-                "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
+                "consider increasing the recursion limit by adding a \
+                 `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
                 suggested_limit, self.cx.ecfg.crate_name,
             ))
             .emit();
index d69a247054026f9a403ecbd9851d8f0416e15fff..814054c551878a47a47c861ea3b0c6ce244580ea 100644 (file)
@@ -300,7 +300,7 @@ pub fn extract<'a, F>(check_name: F, attrs: &'a [ast::Attribute]) -> Option<(Sym
     Oom,                     sym::oom,                 oom,                        Target::Fn,             GenericRequirement::None;
     AllocLayout,             sym::alloc_layout,        alloc_layout,               Target::Struct,         GenericRequirement::None;
 
-    Start,                   sym::start,               start_fn,                   Target::Fn,             GenericRequirement::None;
+    Start,                   sym::start,               start_fn,                   Target::Fn,             GenericRequirement::Exact(1);
 
     EhPersonality,           sym::eh_personality,      eh_personality,             Target::Fn,             GenericRequirement::None;
     EhCatchTypeinfo,         sym::eh_catch_typeinfo,   eh_catch_typeinfo,          Target::Static,         GenericRequirement::None;
index 11ee8fb17ad1b4abc01175e138d765b28e192bb1..cff848eeb6a0fc818cb718c2b396cdb3ce41308f 100644 (file)
@@ -187,11 +187,11 @@ pub fn trace_exp<T>(self, a_is_expected: bool, a: T, b: T) -> Trace<'a, 'tcx>
 impl<'a, 'tcx> Trace<'a, 'tcx> {
     /// Makes `a <: b` where `a` may or may not be expected (if
     /// `a_is_expected` is true, then `a` is expected).
+    #[instrument(skip(self), level = "debug")]
     pub fn sub<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
     where
         T: Relate<'tcx>,
     {
-        debug!("sub({:?} <: {:?})", a, b);
         let Trace { at, trace, a_is_expected } = self;
         at.infcx.commit_if_ok(|_| {
             let mut fields = at.infcx.combine_fields(trace, at.param_env);
@@ -204,11 +204,11 @@ pub fn sub<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
 
     /// Makes `a == b`; the expectation is set by the call to
     /// `trace()`.
+    #[instrument(skip(self), level = "debug")]
     pub fn eq<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
     where
         T: Relate<'tcx>,
     {
-        debug!("eq({:?} == {:?})", a, b);
         let Trace { at, trace, a_is_expected } = self;
         at.infcx.commit_if_ok(|_| {
             let mut fields = at.infcx.combine_fields(trace, at.param_env);
@@ -219,11 +219,11 @@ pub fn eq<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
         })
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub fn lub<T>(self, a: T, b: T) -> InferResult<'tcx, T>
     where
         T: Relate<'tcx>,
     {
-        debug!("lub({:?} \\/ {:?})", a, b);
         let Trace { at, trace, a_is_expected } = self;
         at.infcx.commit_if_ok(|_| {
             let mut fields = at.infcx.combine_fields(trace, at.param_env);
@@ -234,11 +234,11 @@ pub fn lub<T>(self, a: T, b: T) -> InferResult<'tcx, T>
         })
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub fn glb<T>(self, a: T, b: T) -> InferResult<'tcx, T>
     where
         T: Relate<'tcx>,
     {
-        debug!("glb({:?} /\\ {:?})", a, b);
         let Trace { at, trace, a_is_expected } = self;
         at.infcx.commit_if_ok(|_| {
             let mut fields = at.infcx.combine_fields(trace, at.param_env);
index b5c0307255771d3a4d9da59c21e6ca98df076488..2296cc6129ae8594a70f579b546d307a844418ad 100644 (file)
@@ -49,6 +49,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
     ///   the same thing happens, but the resulting query is marked as ambiguous.
     /// - Finally, if any of the obligations result in a hard error,
     ///   then `Err(NoSolution)` is returned.
+    #[instrument(skip(self, inference_vars, answer, fulfill_cx), level = "trace")]
     pub fn make_canonicalized_query_response<T>(
         &self,
         inference_vars: CanonicalVarValues<'tcx>,
@@ -62,7 +63,7 @@ pub fn make_canonicalized_query_response<T>(
         let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?;
         let canonical_result = self.canonicalize_response(query_response);
 
-        debug!("make_canonicalized_query_response: canonical_result = {:#?}", canonical_result);
+        debug!("canonical_result = {:#?}", canonical_result);
 
         Ok(self.tcx.arena.alloc(canonical_result))
     }
@@ -94,6 +95,7 @@ pub fn make_query_response_ignoring_pending_obligations<T>(
 
     /// Helper for `make_canonicalized_query_response` that does
     /// everything up until the final canonicalization.
+    #[instrument(skip(self, fulfill_cx), level = "debug")]
     fn make_query_response<T>(
         &self,
         inference_vars: CanonicalVarValues<'tcx>,
@@ -105,13 +107,6 @@ fn make_query_response<T>(
     {
         let tcx = self.tcx;
 
-        debug!(
-            "make_query_response(\
-             inference_vars={:?}, \
-             answer={:?})",
-            inference_vars, answer,
-        );
-
         // Select everything, returning errors.
         let true_errors = fulfill_cx.select_where_possible(self).err().unwrap_or_else(Vec::new);
         debug!("true_errors = {:#?}", true_errors);
index 35ebe92c59246d9fd9f585696fc3634f691018d7..773753a0363264400be63ce562bae19850ffa3e8 100644 (file)
@@ -94,13 +94,12 @@ fn variable_lengths(&self) -> VariableLengths {
     /// the actual types (`?T`, `Option<?T>`) -- and remember that
     /// after the snapshot is popped, the variable `?T` is no longer
     /// unified.
+    #[instrument(skip(self, f), level = "debug")]
     pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
     where
         F: FnOnce() -> Result<T, E>,
         T: TypeFoldable<'tcx>,
     {
-        debug!("fudge_inference_if_ok()");
-
         let variable_lengths = self.variable_lengths();
         let (mut fudger, value) = self.probe(|_| {
             match f() {
index d460222df8ad0f82508f7ae52cdd6fa9f63c029d..ae85e55da6ae321b7a8889ca74e009b1795d401c 100644 (file)
@@ -9,6 +9,7 @@
 use rustc_middle::ty::{self, Binder, TypeFoldable};
 
 impl<'a, 'tcx> CombineFields<'a, 'tcx> {
+    #[instrument(skip(self), level = "debug")]
     pub fn higher_ranked_sub<T>(
         &mut self,
         a: Binder<'tcx, T>,
@@ -18,8 +19,6 @@ pub fn higher_ranked_sub<T>(
     where
         T: Relate<'tcx>,
     {
-        debug!("higher_ranked_sub(a={:?}, b={:?})", a, b);
-
         // Rather than checking the subtype relationship between `a` and `b`
         // as-is, we need to do some extra work here in order to make sure
         // that function subtyping works correctly with respect to regions
index 632e792bbd1acadccd66b06465116facb2ee18a1..18836d5a68e26e8cdf67f5f1b18c9182ef6c54a9 100644 (file)
@@ -807,8 +807,8 @@ fn start_snapshot(&self) -> CombinedSnapshot<'a, 'tcx> {
         }
     }
 
+    #[instrument(skip(self, snapshot), level = "debug")]
     fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) {
-        debug!("rollback_to(cause={})", cause);
         let CombinedSnapshot {
             undo_snapshot,
             region_constraints_snapshot,
@@ -825,8 +825,8 @@ fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) {
         inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot);
     }
 
+    #[instrument(skip(self, snapshot), level = "debug")]
     fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) {
-        debug!("commit_from()");
         let CombinedSnapshot {
             undo_snapshot,
             region_constraints_snapshot: _,
@@ -841,11 +841,11 @@ fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) {
     }
 
     /// Executes `f` and commit the bindings.
+    #[instrument(skip(self, f), level = "debug")]
     pub fn commit_unconditionally<R, F>(&self, f: F) -> R
     where
         F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
     {
-        debug!("commit_unconditionally()");
         let snapshot = self.start_snapshot();
         let r = f(&snapshot);
         self.commit_from(snapshot);
@@ -853,11 +853,11 @@ pub fn commit_unconditionally<R, F>(&self, f: F) -> R
     }
 
     /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`.
+    #[instrument(skip(self, f), level = "debug")]
     pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
     where
         F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> Result<T, E>,
     {
-        debug!("commit_if_ok()");
         let snapshot = self.start_snapshot();
         let r = f(&snapshot);
         debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok());
@@ -873,11 +873,11 @@ pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
     }
 
     /// Execute `f` then unroll any bindings it creates.
+    #[instrument(skip(self, f), level = "debug")]
     pub fn probe<R, F>(&self, f: F) -> R
     where
         F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
     {
-        debug!("probe()");
         let snapshot = self.start_snapshot();
         let r = f(&snapshot);
         self.rollback_to("probe", snapshot);
@@ -885,11 +885,11 @@ pub fn probe<R, F>(&self, f: F) -> R
     }
 
     /// If `should_skip` is true, then execute `f` then unroll any bindings it creates.
+    #[instrument(skip(self, f), level = "debug")]
     pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R
     where
         F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
     {
-        debug!("probe()");
         let snapshot = self.start_snapshot();
         let was_skip_leak_check = self.skip_leak_check.get();
         if should_skip {
@@ -946,18 +946,19 @@ pub fn can_eq<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> UnitResult
         })
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub fn sub_regions(
         &self,
         origin: SubregionOrigin<'tcx>,
         a: ty::Region<'tcx>,
         b: ty::Region<'tcx>,
     ) {
-        debug!("sub_regions({:?} <: {:?})", a, b);
         self.inner.borrow_mut().unwrap_region_constraints().make_subregion(origin, a, b);
     }
 
     /// Require that the region `r` be equal to one of the regions in
     /// the set `regions`.
+    #[instrument(skip(self), level = "debug")]
     pub fn member_constraint(
         &self,
         opaque_type_def_id: DefId,
@@ -966,7 +967,6 @@ pub fn member_constraint(
         region: ty::Region<'tcx>,
         in_regions: &Lrc<Vec<ty::Region<'tcx>>>,
     ) {
-        debug!("member_constraint({:?} <: {:?})", region, in_regions);
         self.inner.borrow_mut().unwrap_region_constraints().member_constraint(
             opaque_type_def_id,
             definition_span,
index 73d74584a5e13db33c74ced5712e7d8b7427eed4..29a9cbc7a99c368be1fcaaeea7fabf3a0bdeeff3 100644 (file)
@@ -507,6 +507,7 @@ fn a_is_expected(&self) -> bool {
         true
     }
 
+    #[instrument(skip(self, info), level = "trace")]
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         variance: ty::Variance,
@@ -514,23 +515,22 @@ fn relate_with_variance<T: Relate<'tcx>>(
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
-        debug!("relate_with_variance(variance={:?}, a={:?}, b={:?})", variance, a, b);
-
         let old_ambient_variance = self.ambient_variance;
         self.ambient_variance = self.ambient_variance.xform(variance);
         self.ambient_variance_info = self.ambient_variance_info.xform(info);
 
-        debug!("relate_with_variance: ambient_variance = {:?}", self.ambient_variance);
+        debug!(?self.ambient_variance);
 
         let r = self.relate(a, b)?;
 
         self.ambient_variance = old_ambient_variance;
 
-        debug!("relate_with_variance: r={:?}", r);
+        debug!(?r);
 
         Ok(r)
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
         let a = self.infcx.shallow_resolve(a);
 
@@ -573,7 +573,7 @@ fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>>
             }
 
             _ => {
-                debug!("tys(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance);
+                debug!(?a, ?b, ?self.ambient_variance);
 
                 // Will also handle unification of `IntVar` and `FloatVar`.
                 self.infcx.super_combine_tys(self, a, b)
@@ -581,18 +581,19 @@ fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>>
         }
     }
 
+    #[instrument(skip(self), level = "trace")]
     fn regions(
         &mut self,
         a: ty::Region<'tcx>,
         b: ty::Region<'tcx>,
     ) -> RelateResult<'tcx, ty::Region<'tcx>> {
-        debug!("regions(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance);
+        debug!(?self.ambient_variance);
 
         let v_a = self.replace_bound_region(a, ty::INNERMOST, &self.a_scopes);
         let v_b = self.replace_bound_region(b, ty::INNERMOST, &self.b_scopes);
 
-        debug!("regions: v_a = {:?}", v_a);
-        debug!("regions: v_b = {:?}", v_b);
+        debug!(?v_a);
+        debug!(?v_b);
 
         if self.ambient_covariance() {
             // Covariance: a <= b. Hence, `b: a`.
@@ -628,6 +629,7 @@ fn consts(
         }
     }
 
+    #[instrument(skip(self), level = "trace")]
     fn binders<T>(
         &mut self,
         a: ty::Binder<'tcx, T>,
@@ -655,7 +657,7 @@ fn binders<T>(
         // - Instantiate binders on `b` universally, yielding a universe U1.
         // - Instantiate binders on `a` existentially in U1.
 
-        debug!("binders({:?}: {:?}, ambient_variance={:?})", a, b, self.ambient_variance);
+        debug!(?self.ambient_variance);
 
         if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) {
             // Fast path for the common case.
@@ -673,8 +675,8 @@ fn binders<T>(
             let b_scope = self.create_scope(b, UniversallyQuantified(true));
             let a_scope = self.create_scope(a, UniversallyQuantified(false));
 
-            debug!("binders: a_scope = {:?} (existential)", a_scope);
-            debug!("binders: b_scope = {:?} (universal)", b_scope);
+            debug!(?a_scope, "(existential)");
+            debug!(?b_scope, "(universal)");
 
             self.b_scopes.push(b_scope);
             self.a_scopes.push(a_scope);
@@ -717,8 +719,8 @@ fn binders<T>(
             let a_scope = self.create_scope(a, UniversallyQuantified(true));
             let b_scope = self.create_scope(b, UniversallyQuantified(false));
 
-            debug!("binders: a_scope = {:?} (universal)", a_scope);
-            debug!("binders: b_scope = {:?} (existential)", b_scope);
+            debug!(?a_scope, "(universal)");
+            debug!(?b_scope, "(existential)");
 
             self.a_scopes.push(a_scope);
             self.b_scopes.push(b_scope);
index af31ab0923decb72c580212b932ec59964133d5b..df4fdb3a982ecaabd2e839be78a20e9f6362e574 100644 (file)
@@ -540,6 +540,7 @@ pub fn member_constraint(
         });
     }
 
+    #[instrument(skip(self, origin), level = "debug")]
     pub fn make_subregion(
         &mut self,
         origin: SubregionOrigin<'tcx>,
@@ -547,10 +548,7 @@ pub fn make_subregion(
         sup: Region<'tcx>,
     ) {
         // cannot add constraints once regions are resolved
-        debug!(
-            "RegionConstraintCollector: make_subregion({:?}, {:?}) due to {:?}",
-            sub, sup, origin
-        );
+        debug!("origin = {:#?}", origin);
 
         match (sub, sup) {
             (&ReLateBound(..), _) | (_, &ReLateBound(..)) => {
index 45ad2df82455b68f95ade12e96bc45c7924dbd69..55a3fcd0266c4acbdcc39b6a879ecd15d974b3f9 100644 (file)
@@ -16,9 +16,9 @@
 use rustc_hir::Crate;
 use rustc_lint::LintStore;
 use rustc_metadata::creader::CStore;
+use rustc_metadata::{encode_metadata, EncodedMetadata};
 use rustc_middle::arena::Arena;
 use rustc_middle::dep_graph::DepGraph;
-use rustc_middle::middle;
 use rustc_middle::middle::cstore::{MetadataLoader, MetadataLoaderDyn};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt};
@@ -179,7 +179,7 @@ pub fn register_plugins<'a>(
     register_lints: impl Fn(&Session, &mut LintStore),
     mut krate: ast::Crate,
     crate_name: &str,
-) -> Result<(ast::Crate, Lrc<LintStore>)> {
+) -> Result<(ast::Crate, LintStore)> {
     krate = sess.time("attributes_injection", || {
         rustc_builtin_macros::cmdline_attrs::inject(
             krate,
@@ -230,9 +230,6 @@ pub fn register_plugins<'a>(
         }
     });
 
-    let lint_store = Lrc::new(lint_store);
-    sess.init_lint_store(lint_store.clone());
-
     Ok((krate, lint_store))
 }
 
@@ -980,7 +977,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
 fn encode_and_write_metadata(
     tcx: TyCtxt<'_>,
     outputs: &OutputFilenames,
-) -> (middle::cstore::EncodedMetadata, bool) {
+) -> (EncodedMetadata, bool) {
     #[derive(PartialEq, Eq, PartialOrd, Ord)]
     enum MetadataKind {
         None,
@@ -1003,8 +1000,8 @@ enum MetadataKind {
         .unwrap_or(MetadataKind::None);
 
     let metadata = match metadata_kind {
-        MetadataKind::None => middle::cstore::EncodedMetadata::new(),
-        MetadataKind::Uncompressed | MetadataKind::Compressed => tcx.encode_metadata(),
+        MetadataKind::None => EncodedMetadata::new(),
+        MetadataKind::Uncompressed | MetadataKind::Compressed => encode_metadata(tcx),
     };
 
     let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");
@@ -1023,7 +1020,7 @@ enum MetadataKind {
             .tempdir_in(out_filename.parent().unwrap())
             .unwrap_or_else(|err| tcx.sess.fatal(&format!("couldn't create a temp dir: {}", err)));
         let metadata_tmpdir = MaybeTempDir::new(metadata_tmpdir, tcx.sess.opts.cg.save_temps);
-        let metadata_filename = emit_metadata(tcx.sess, &metadata.raw_data, &metadata_tmpdir);
+        let metadata_filename = emit_metadata(tcx.sess, metadata.raw_data(), &metadata_tmpdir);
         if let Err(e) = util::non_durable_rename(&metadata_filename, &out_filename) {
             tcx.sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e));
         }
index 2f540395b2d79f082b1c398c5d62933257733d9b..a71b59b9d49bdc0e2efad8104d59e6f0eb491340 100644 (file)
@@ -135,7 +135,7 @@ pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, Lrc<LintStore>)>> {
             let krate = self.parse()?.take();
 
             let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {};
-            let result = passes::register_plugins(
+            let (krate, lint_store) = passes::register_plugins(
                 self.session(),
                 &*self.codegen_backend().metadata_loader(),
                 self.compiler.register_lints.as_deref().unwrap_or_else(|| empty),
@@ -150,7 +150,7 @@ pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, Lrc<LintStore>)>> {
             // called, which happens within passes::register_plugins().
             self.dep_graph_future().ok();
 
-            Ok(result)
+            Ok((krate, Lrc::new(lint_store)))
         })
     }
 
index 8cbd2ddcbfdf733028ac1f350df837d2471eadb1..d235b2209444eb8bd7dfb7e6896d416e95597499 100644 (file)
@@ -38,7 +38,6 @@
 use rustc_session::lint::{BuiltinLintDiagnostics, ExternDepSpec};
 use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
 use rustc_session::Session;
-use rustc_session::SessionLintStore;
 use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP};
 use rustc_target::abi;
@@ -75,20 +74,6 @@ pub struct LintStore {
     lint_groups: FxHashMap<&'static str, LintGroup>,
 }
 
-impl SessionLintStore for LintStore {
-    fn name_to_lint(&self, lint_name: &str) -> LintId {
-        let lints = self
-            .find_lints(lint_name)
-            .unwrap_or_else(|_| panic!("Failed to find lint with name `{}`", lint_name));
-
-        if let &[lint] = lints.as_slice() {
-            return lint;
-        } else {
-            panic!("Found mutliple lints with name `{}`: {:?}", lint_name, lints);
-        }
-    }
-}
-
 /// The target of the `by_name` map, which accounts for renaming/deprecation.
 #[derive(Debug)]
 enum TargetLint {
index 48eb50953a957dc759f4e867a9a3564c9fe6a192..ddb5f7dcebfad5777cb9e364513c53f40ab4294f 100644 (file)
@@ -1572,7 +1572,11 @@ extern "C" bool
 LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) {
   Module &Mod = *unwrap(M);
   const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier());
+#if LLVM_VERSION_GE(14, 0)
+  thinLTOFinalizeInModule(Mod, DefinedGlobals, /*PropagateAttrs=*/true);
+#else
   thinLTOResolvePrevailingInModule(Mod, DefinedGlobals);
+#endif
   return true;
 }
 
index 2c9bad7e5cedb326da042688f939998a06a9f140..644b849a9f899e17bb81ccfec572c1591cc64489 100644 (file)
@@ -30,4 +30,4 @@
 pub mod dynamic_lib;
 pub mod locator;
 
-pub use rmeta::METADATA_HEADER;
+pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER};
index 7be0e32ef38dca3c9753defe7730ee817123e067..bd1d99640f81d6256fc55b6e5864a33f8e7550da 100644 (file)
@@ -1,7 +1,6 @@
 use crate::creader::{CStore, LoadedMacro};
 use crate::foreign_modules;
 use crate::native_libs;
-use crate::rmeta::encoder;
 
 use rustc_ast as ast;
 use rustc_data_structures::stable_map::FxHashMap;
@@ -10,7 +9,7 @@
 use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
 use rustc_middle::hir::exports::Export;
 use rustc_middle::middle::cstore::ForeignModule;
-use rustc_middle::middle::cstore::{CrateSource, CrateStore, EncodedMetadata};
+use rustc_middle::middle::cstore::{CrateSource, CrateStore};
 use rustc_middle::middle::exported_symbols::ExportedSymbol;
 use rustc_middle::middle::stability::DeprecationEntry;
 use rustc_middle::ty::query::Providers;
@@ -511,8 +510,4 @@ fn def_path_hash_to_def_id(&self, cnum: CrateNum, hash: DefPathHash) -> DefId {
     fn expn_hash_to_expn_id(&self, cnum: CrateNum, index_guess: u32, hash: ExpnHash) -> ExpnId {
         self.get_crate_data(cnum).expn_hash_to_expn_id(index_guess, hash)
     }
-
-    fn encode_metadata(&self, tcx: TyCtxt<'_>) -> EncodedMetadata {
-        encoder::encode_metadata(tcx)
-    }
 }
index 8509aa00bc0225de605385211c021cf16ab8f98e..1e3bf8aca8bdcbd204f58179db0f09eb0fdde22c 100644 (file)
@@ -18,7 +18,7 @@
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_index::vec::Idx;
 use rustc_middle::hir::map::Map;
-use rustc_middle::middle::cstore::{EncodedMetadata, ForeignModule, LinkagePreference, NativeLib};
+use rustc_middle::middle::cstore::{ForeignModule, LinkagePreference, NativeLib};
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::{
     metadata_symbol_name, ExportedSymbol, SymbolExportLevel,
@@ -2101,7 +2101,26 @@ fn prefetch_mir(tcx: TyCtxt<'_>) {
 // will allow us to slice the metadata to the precise length that we just
 // generated regardless of trailing bytes that end up in it.
 
-pub(super) fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata {
+#[derive(Encodable, Decodable)]
+pub struct EncodedMetadata {
+    raw_data: Vec<u8>,
+}
+
+impl EncodedMetadata {
+    #[inline]
+    pub fn new() -> EncodedMetadata {
+        EncodedMetadata { raw_data: Vec::new() }
+    }
+
+    #[inline]
+    pub fn raw_data(&self) -> &[u8] {
+        &self.raw_data[..]
+    }
+}
+
+pub fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata {
+    let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata");
+
     // Since encoding metadata is not in a query, and nothing is cached,
     // there's no need to do dep-graph tracking for any of it.
     tcx.dep_graph.assert_ignored();
index eb2bd80f46e64686715184798f10db490e3935e0..af06e1cf3f98020e307798ddb321f15f107a2dab 100644 (file)
@@ -33,6 +33,7 @@
 pub use decoder::{provide, provide_extern};
 crate use decoder::{CrateMetadata, CrateNumMap, MetadataBlob};
 use encoder::EncodeContext;
+pub use encoder::{encode_metadata, EncodedMetadata};
 use rustc_span::hygiene::SyntaxContextData;
 
 mod decoder;
index 81c44b27033ec07252961490db2a035e2af1b218..2a1bb43a466b09638a995efc9ebc829d16c3f93d 100644 (file)
@@ -2,8 +2,6 @@
 //! are *mostly* used as a part of that interface, but these should
 //! probably get a better home if someone can find one.
 
-use crate::ty::TyCtxt;
-
 use rustc_ast as ast;
 use rustc_data_structures::sync::{self, MetadataRef};
 use rustc_hir::def_id::{CrateNum, DefId, StableCrateId, LOCAL_CRATE};
@@ -150,17 +148,6 @@ pub enum ExternCrateSource {
     Path,
 }
 
-#[derive(Encodable, Decodable)]
-pub struct EncodedMetadata {
-    pub raw_data: Vec<u8>,
-}
-
-impl EncodedMetadata {
-    pub fn new() -> EncodedMetadata {
-        EncodedMetadata { raw_data: Vec::new() }
-    }
-}
-
 /// The backend's way to give the crate store access to the metadata in a library.
 /// Note that it returns the raw metadata bytes stored in the library file, whether
 /// it is compressed, uncompressed, some weird mix, etc.
@@ -204,9 +191,6 @@ pub trait CrateStore: std::fmt::Debug {
     /// Fetch a DefId from a DefPathHash for a foreign crate.
     fn def_path_hash_to_def_id(&self, cnum: CrateNum, hash: DefPathHash) -> DefId;
     fn expn_hash_to_expn_id(&self, cnum: CrateNum, index_guess: u32, hash: ExpnHash) -> ExpnId;
-
-    // utility functions
-    fn encode_metadata(&self, tcx: TyCtxt<'_>) -> EncodedMetadata;
 }
 
 pub type CrateStoreDyn = dyn CrateStore + sync::Sync;
index 72b8d7cce7142d93c7ee5e7c9a09c57ee72362ae..1e8ae81333673e26926c5ce92f1bc068064d2253 100644 (file)
@@ -7,7 +7,6 @@
 use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
 use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource};
 use crate::middle;
-use crate::middle::cstore::EncodedMetadata;
 use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath, ObjectLifetimeDefault};
 use crate::middle::stability;
 use crate::mir::interpret::{self, AllocId, Allocation, ConstValue, Scalar};
@@ -1324,11 +1323,6 @@ pub fn def_path_debug_str(self, def_id: DefId) -> String {
         )
     }
 
-    pub fn encode_metadata(self) -> EncodedMetadata {
-        let _prof_timer = self.prof.verbose_generic_activity("generate_crate_metadata");
-        self.untracked_resolutions.cstore.encode_metadata(self)
-    }
-
     /// Note that this is *untracked* and should only be used within the query
     /// system if the result is otherwise tracked through queries
     pub fn cstore_untracked(self) -> &'tcx ty::CrateStoreDyn {
index a04b0a7ef61363b39226653c0a3a8df3981d36f6..e16491dcc90b25eeb62ff3858833d11fbfb406f2 100644 (file)
@@ -79,6 +79,7 @@ fn definitely_has_type_flags(&self, tcx: TyCtxt<'tcx>, flags: TypeFlags) -> bool
             == Some(FoundFlags)
     }
 
+    #[instrument(level = "trace")]
     fn has_type_flags(&self, flags: TypeFlags) -> bool {
         self.visit_with(&mut HasTypeFlagsVisitor { tcx: None, flags }).break_value()
             == Some(FoundFlags)
@@ -476,21 +477,16 @@ fn fold_binder<T: TypeFoldable<'tcx>>(
         t
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match *r {
             ty::ReLateBound(debruijn, _) if debruijn < self.current_index => {
-                debug!(
-                    "RegionFolder.fold_region({:?}) skipped bound region (current index={:?})",
-                    r, self.current_index
-                );
+                debug!(?self.current_index, "skipped bound region");
                 *self.skipped_regions = true;
                 r
             }
             _ => {
-                debug!(
-                    "RegionFolder.fold_region({:?}) folding free region (current_index={:?})",
-                    r, self.current_index
-                );
+                debug!(?self.current_index, "folding free region");
                 (self.fold_region_fn)(r, self.current_index)
             }
         }
@@ -1125,6 +1121,12 @@ struct HasTypeFlagsVisitor<'tcx> {
     flags: ty::TypeFlags,
 }
 
+impl std::fmt::Debug for HasTypeFlagsVisitor<'tcx> {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.flags.fmt(fmt)
+    }
+}
+
 impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor<'tcx> {
     type BreakTy = FoundFlags;
     fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
@@ -1132,9 +1134,10 @@ fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
     }
 
     #[inline]
+    #[instrument(level = "trace")]
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         let flags = t.flags();
-        debug!("HasTypeFlagsVisitor: t={:?} flags={:?} self.flags={:?}", t, flags, self.flags);
+        trace!(t.flags=?t.flags());
         if flags.intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
         } else {
@@ -1146,9 +1149,10 @@ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
     }
 
     #[inline]
+    #[instrument(skip(self), level = "trace")]
     fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
         let flags = r.type_flags();
-        debug!("HasTypeFlagsVisitor: r={:?} r.flags={:?} self.flags={:?}", r, flags, self.flags);
+        trace!(r.flags=?flags);
         if flags.intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
         } else {
@@ -1157,9 +1161,10 @@ fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
     }
 
     #[inline]
+    #[instrument(level = "trace")]
     fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
         let flags = FlagComputation::for_const(c);
-        debug!("HasTypeFlagsVisitor: c={:?} c.flags={:?} self.flags={:?}", c, flags, self.flags);
+        trace!(r.flags=?flags);
         if flags.intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
         } else {
@@ -1171,9 +1176,10 @@ fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy
     }
 
     #[inline]
+    #[instrument(level = "trace")]
     fn visit_unevaluated_const(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> {
         let flags = FlagComputation::for_unevaluated_const(uv);
-        debug!("HasTypeFlagsVisitor: uv={:?} uv.flags={:?} self.flags={:?}", uv, flags, self.flags);
+        trace!(r.flags=?flags);
         if flags.intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
         } else {
@@ -1185,12 +1191,10 @@ fn visit_unevaluated_const(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<
     }
 
     #[inline]
+    #[instrument(level = "trace")]
     fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
         let flags = predicate.inner.flags;
-        debug!(
-            "HasTypeFlagsVisitor: predicate={:?} flags={:?} self.flags={:?}",
-            predicate, flags, self.flags
-        );
+        trace!(predicate.flags=?flags);
         if flags.intersects(self.flags) {
             ControlFlow::Break(FoundFlags)
         } else {
index 13e2122a619dcf3bd097c8ad56cfd207c385c5bc..308b4d2fefc71c7ade6cf71e8e84ddaf1377f0e2 100644 (file)
@@ -98,14 +98,14 @@ fn path_generic_args(
 
     // Defaults (should not be overridden):
 
+    #[instrument(skip(self), level = "debug")]
     fn default_print_def_path(
         self,
         def_id: DefId,
         substs: &'tcx [GenericArg<'tcx>],
     ) -> Result<Self::Path, Self::Error> {
-        debug!("default_print_def_path: def_id={:?}, substs={:?}", def_id, substs);
         let key = self.tcx().def_key(def_id);
-        debug!("default_print_def_path: key={:?}", key);
+        debug!(?key);
 
         match key.disambiguated_data.data {
             DefPathData::CrateRoot => {
index d99534c200a5a557c8e1dcff2d1241c8ca00071a..b8bca6363db802e7a62db0c5c98edda3fb0f35f3 100644 (file)
@@ -2033,12 +2033,11 @@ pub fn pretty_wrap_binder<T, C: Fn(&T, Self) -> Result<Self, fmt::Error>>(
         Ok(inner)
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn prepare_late_bound_region_info<T>(&mut self, value: &ty::Binder<'tcx, T>)
     where
         T: TypeFoldable<'tcx>,
     {
-        debug!("prepare_late_bound_region_info(value: {:?})", value);
-
         struct LateBoundRegionNameCollector<'a, 'tcx> {
             tcx: TyCtxt<'tcx>,
             used_region_names: &'a mut FxHashSet<Symbol>,
@@ -2052,8 +2051,9 @@ fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
                 Some(self.tcx)
             }
 
+            #[instrument(skip(self), level = "trace")]
             fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
-                debug!("LateBoundRegionNameCollector::visit_region(r: {:?}, address: {:p})", r, &r);
+                trace!("address: {:p}", r);
                 if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) = *r {
                     self.used_region_names.insert(name);
                 } else if let ty::RePlaceholder(ty::PlaceholderRegion {
@@ -2068,8 +2068,8 @@ fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
 
             // We collect types in order to prevent really large types from compiling for
             // a really long time. See issue #83150 for why this is necessary.
+            #[instrument(skip(self), level = "trace")]
             fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-                debug!("LateBoundRegionNameCollector::visit_ty(ty: {:?}", ty);
                 let not_previously_inserted = self.type_collector.insert(ty);
                 if not_previously_inserted {
                     ty.super_visit_with(self)
index 2ec06d472fee0b9889f33ff146c267d2ee707f82..d4032cdf696cdc6e7c28c225e02b151538ed46ce 100644 (file)
@@ -516,6 +516,7 @@ pub fn static_ptr_ty(self, def_id: DefId) -> Ty<'tcx> {
     }
 
     /// Expands the given impl trait type, stopping if the type is recursive.
+    #[instrument(skip(self), level = "debug")]
     pub fn try_expand_impl_trait_type(
         self,
         def_id: DefId,
@@ -532,6 +533,7 @@ pub fn try_expand_impl_trait_type(
         };
 
         let expanded_type = visitor.expand_opaque_ty(def_id, substs).unwrap();
+        trace!(?expanded_type);
         if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
     }
 }
index 1803a18441ce20cffc8fba87ca09fae02193d7b8..53868f285576392751518368d4168bfe39dd81a1 100644 (file)
@@ -449,8 +449,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     })
                     .collect();
 
-                let destination = this.cfg.start_new_block();
+                if !options.contains(InlineAsmOptions::NORETURN) {
+                    this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
+                }
 
+                let destination_block = this.cfg.start_new_block();
                 this.cfg.terminate(
                     block,
                     source_info,
@@ -462,11 +465,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         destination: if options.contains(InlineAsmOptions::NORETURN) {
                             None
                         } else {
-                            Some(destination)
+                            Some(destination_block)
                         },
                     },
                 );
-                destination.unit()
+                destination_block.unit()
             }
 
             // These cases don't actually need a destination
index 66005be05df75683ba3427c2c9719262317287fc..17296a95bc17e26e2c6560ea91001e977c8841e7 100644 (file)
@@ -39,10 +39,17 @@ pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> E
 
         let mut expr = self.make_mirror_unadjusted(hir_expr);
 
+        let adjustment_span = match self.adjustment_span {
+            Some((hir_id, span)) if hir_id == hir_expr.hir_id => Some(span),
+            _ => None,
+        };
+
         // Now apply adjustments, if any.
         for adjustment in self.typeck_results.expr_adjustments(hir_expr) {
             debug!("make_mirror: expr={:?} applying adjustment={:?}", expr, adjustment);
-            expr = self.apply_adjustment(hir_expr, expr, adjustment);
+            let span = expr.span;
+            expr =
+                self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span));
         }
 
         // Next, wrap this up in the expr's scope.
@@ -82,8 +89,9 @@ fn apply_adjustment(
         hir_expr: &'tcx hir::Expr<'tcx>,
         mut expr: Expr<'tcx>,
         adjustment: &Adjustment<'tcx>,
+        mut span: Span,
     ) -> Expr<'tcx> {
-        let Expr { temp_lifetime, mut span, .. } = expr;
+        let Expr { temp_lifetime, .. } = expr;
 
         // Adjust the span from the block, to the last expression of the
         // block. This is a better span when returning a mutable reference
@@ -150,6 +158,7 @@ fn apply_adjustment(
 
     fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
         let expr_ty = self.typeck_results().expr_ty(expr);
+        let expr_span = expr.span;
         let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
 
         let kind = match expr.kind {
@@ -157,7 +166,13 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
             hir::ExprKind::MethodCall(_, method_span, ref args, fn_span) => {
                 // Rewrite a.b(c) into UFCS form like Trait::b(a, c)
                 let expr = self.method_callee(expr, method_span, None);
+                // When we apply adjustments to the receiver, use the span of
+                // the overall method call for better diagnostics. args[0]
+                // is guaranteed to exist, since a method call always has a receiver.
+                let old_adjustment_span = self.adjustment_span.replace((args[0].hir_id, expr_span));
+                tracing::info!("Using method span: {:?}", expr.span);
                 let args = self.mirror_exprs(args);
+                self.adjustment_span = old_adjustment_span;
                 ExprKind::Call {
                     ty: expr.ty,
                     fun: self.thir.exprs.push(expr),
index 5059dd939d92d7ce005e1473c8cbbee263ba6d28..38a4676bd1561c869bbbb79f0ea8f01bf9061eb7 100644 (file)
@@ -9,6 +9,7 @@
 use rustc_data_structures::steal::Steal;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::HirId;
 use rustc_hir::Node;
 use rustc_middle::middle::region;
 use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
@@ -46,6 +47,14 @@ struct Cx<'tcx> {
     crate region_scope_tree: &'tcx region::ScopeTree,
     crate typeck_results: &'tcx ty::TypeckResults<'tcx>,
 
+    /// When applying adjustments to the expression
+    /// with the given `HirId`, use the given `Span`,
+    /// instead of the usual span. This is used to
+    /// assign the span of an overall method call
+    /// (e.g. `my_val.foo()`) to the adjustment expressions
+    /// for the receiver.
+    adjustment_span: Option<(HirId, Span)>,
+
     /// The `DefId` of the owner of this body.
     body_owner: DefId,
 }
@@ -60,6 +69,7 @@ fn new(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) -> Cx<'tcx> {
             region_scope_tree: tcx.region_scope_tree(def.did),
             typeck_results,
             body_owner: def.did.to_def_id(),
+            adjustment_span: None,
         }
     }
 
index 4c51b9207bb750b03536b53de29c4183ec1aa2f7..e28fd2c50814fa48428cb9afc2a0649b12ea2be8 100644 (file)
@@ -1,6 +1,6 @@
+use super::deconstruct_pat::{Constructor, DeconstructedPat};
 use super::usefulness::{
-    compute_match_usefulness, expand_pattern, is_wildcard, MatchArm, MatchCheckCtxt, Reachability,
-    UsefulnessReport,
+    compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
 };
 use super::{PatCtxt, PatternError};
 
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::{HirId, Pat};
-use rustc_middle::thir::PatKind;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
 use rustc_session::lint::builtin::{
     BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
 };
 use rustc_session::Session;
 use rustc_span::{DesugaringKind, ExpnKind, Span};
-use std::slice;
 
 crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
     let body_id = match def_id.as_local() {
         Some(id) => tcx.hir().body_owned_by(tcx.hir().local_def_id_to_hir_id(id)),
     };
 
+    let pattern_arena = TypedArena::default();
     let mut visitor = MatchVisitor {
         tcx,
         typeck_results: tcx.typeck_body(body_id),
         param_env: tcx.param_env(def_id),
-        pattern_arena: TypedArena::default(),
+        pattern_arena: &pattern_arena,
     };
     visitor.visit_body(tcx.hir().body(body_id));
 }
@@ -40,14 +39,21 @@ fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBu
     struct_span_err!(sess, sp, E0004, "{}", &error_message)
 }
 
-struct MatchVisitor<'a, 'tcx> {
+#[derive(PartialEq)]
+enum RefutableFlag {
+    Irrefutable,
+    Refutable,
+}
+use RefutableFlag::*;
+
+struct MatchVisitor<'a, 'p, 'tcx> {
     tcx: TyCtxt<'tcx>,
     typeck_results: &'a ty::TypeckResults<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
-    pattern_arena: TypedArena<super::Pat<'tcx>>,
+    pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
 }
 
-impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
+impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
     type Map = intravisit::ErasedMap<'tcx>;
 
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
@@ -74,13 +80,13 @@ fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) {
             hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
         };
         self.check_irrefutable(&loc.pat, msg, sp);
-        self.check_patterns(&loc.pat);
+        self.check_patterns(&loc.pat, Irrefutable);
     }
 
     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
         intravisit::walk_param(self, param);
         self.check_irrefutable(&param.pat, "function argument", None);
-        self.check_patterns(&param.pat);
+        self.check_patterns(&param.pat, Irrefutable);
     }
 }
 
@@ -113,31 +119,30 @@ fn span_e0158(&self, span: Span, text: &str) {
     }
 }
 
-impl<'tcx> MatchVisitor<'_, 'tcx> {
-    fn check_patterns(&self, pat: &Pat<'_>) {
+impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
+    fn check_patterns(&self, pat: &Pat<'_>, rf: RefutableFlag) {
         pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
-        check_for_bindings_named_same_as_variants(self, pat);
+        check_for_bindings_named_same_as_variants(self, pat, rf);
     }
 
-    fn lower_pattern<'p>(
+    fn lower_pattern(
         &self,
         cx: &mut MatchCheckCtxt<'p, 'tcx>,
         pat: &'tcx hir::Pat<'tcx>,
         have_errors: &mut bool,
-    ) -> (&'p super::Pat<'tcx>, Ty<'tcx>) {
+    ) -> &'p DeconstructedPat<'p, 'tcx> {
         let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.typeck_results);
         patcx.include_lint_checks();
         let pattern = patcx.lower_pattern(pat);
-        let pattern_ty = pattern.ty;
-        let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(pattern));
+        let pattern: &_ = cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern));
         if !patcx.errors.is_empty() {
             *have_errors = true;
             patcx.report_inlining_errors();
         }
-        (pattern, pattern_ty)
+        pattern
     }
 
-    fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'_, 'tcx> {
+    fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'p, 'tcx> {
         MatchCheckCtxt {
             tcx: self.tcx,
             param_env: self.param_env,
@@ -147,10 +152,10 @@ fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'_, 'tcx> {
     }
 
     fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) {
-        self.check_patterns(pat);
+        self.check_patterns(pat, Refutable);
         let mut cx = self.new_cx(expr.hir_id);
-        let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
-        check_let_reachability(&mut cx, pat.hir_id, &tpat, span);
+        let tpat = self.lower_pattern(&mut cx, pat, &mut false);
+        check_let_reachability(&mut cx, pat.hir_id, tpat, span);
     }
 
     fn check_match(
@@ -163,11 +168,11 @@ fn check_match(
 
         for arm in arms {
             // Check the arm for some things unrelated to exhaustiveness.
-            self.check_patterns(&arm.pat);
+            self.check_patterns(&arm.pat, Refutable);
             if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
-                self.check_patterns(pat);
-                let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
-                check_let_reachability(&mut cx, pat.hir_id, &tpat, tpat.span);
+                self.check_patterns(pat, Refutable);
+                let tpat = self.lower_pattern(&mut cx, pat, &mut false);
+                check_let_reachability(&mut cx, pat.hir_id, tpat, tpat.span());
             }
         }
 
@@ -176,7 +181,7 @@ fn check_match(
         let arms: Vec<_> = arms
             .iter()
             .map(|hir::Arm { pat, guard, .. }| MatchArm {
-                pat: self.lower_pattern(&mut cx, pat, &mut have_errors).0,
+                pat: self.lower_pattern(&mut cx, pat, &mut have_errors),
                 hir_id: pat.hir_id,
                 has_guard: guard.is_some(),
             })
@@ -190,20 +195,16 @@ fn check_match(
         let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
         let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
 
-        report_arm_reachability(&cx, &report, |_, arm_span, arm_hir_id, catchall| {
-            match source {
-                hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
-                    unreachable_pattern(cx.tcx, arm_span, arm_hir_id, catchall);
-                }
-                // Unreachable patterns in try and await expressions occur when one of
-                // the arms are an uninhabited type. Which is OK.
-                hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
+        match source {
+            hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
+                report_arm_reachability(&cx, &report)
             }
-        });
+            // Unreachable patterns in try and await expressions occur when one of
+            // the arms are an uninhabited type. Which is OK.
+            hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
+        }
 
         // Check if the match is exhaustive.
-        // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
-        // since an empty matrix can occur when there are arms, if those arms all have guards.
         let is_empty_match = arms.is_empty();
         let witnesses = report.non_exhaustiveness_witnesses;
         if !witnesses.is_empty() {
@@ -214,7 +215,8 @@ fn check_match(
     fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
         let mut cx = self.new_cx(pat.hir_id);
 
-        let (pattern, pattern_ty) = self.lower_pattern(&mut cx, pat, &mut false);
+        let pattern = self.lower_pattern(&mut cx, pat, &mut false);
+        let pattern_ty = pattern.ty();
         let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
         let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
 
@@ -226,7 +228,7 @@ fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>
             return;
         }
 
-        let joined_patterns = joined_uncovered_patterns(&witnesses);
+        let joined_patterns = joined_uncovered_patterns(&cx, &witnesses);
         let mut err = struct_span_err!(
             self.tcx.sess,
             pat.span,
@@ -302,7 +304,11 @@ fn const_not_var(
     }
 }
 
-fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
+fn check_for_bindings_named_same_as_variants(
+    cx: &MatchVisitor<'_, '_, '_>,
+    pat: &Pat<'_>,
+    rf: RefutableFlag,
+) {
     pat.walk_always(|p| {
         if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
             if let Some(ty::BindByValue(hir::Mutability::Not)) =
@@ -315,25 +321,31 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa
                             variant.ident == ident && variant.ctor_kind == CtorKind::Const
                         })
                     {
+                        let variant_count = edef.variants.len();
                         cx.tcx.struct_span_lint_hir(
                             BINDINGS_WITH_VARIANT_NAME,
                             p.hir_id,
                             p.span,
                             |lint| {
                                 let ty_path = cx.tcx.def_path_str(edef.did);
-                                lint.build(&format!(
+                                let mut err = lint.build(&format!(
                                     "pattern binding `{}` is named the same as one \
-                                                of the variants of the type `{}`",
+                                                    of the variants of the type `{}`",
                                     ident, ty_path
-                                ))
-                                .code(error_code!(E0170))
-                                .span_suggestion(
-                                    p.span,
-                                    "to match on the variant, qualify the path",
-                                    format!("{}::{}", ty_path, ident),
-                                    Applicability::MachineApplicable,
-                                )
-                                .emit();
+                                ));
+                                err.code(error_code!(E0170));
+                                // If this is an irrefutable pattern, and there's > 1 variant,
+                                // then we can't actually match on this. Applying the below
+                                // suggestion would produce code that breaks on `check_irrefutable`.
+                                if rf == Refutable || variant_count == 1 {
+                                    err.span_suggestion(
+                                        p.span,
+                                        "to match on the variant, qualify the path",
+                                        format!("{}::{}", ty_path, ident),
+                                        Applicability::MachineApplicable,
+                                    );
+                                }
+                                err.emit();
                             },
                         )
                     }
@@ -344,12 +356,11 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa
 }
 
 /// Checks for common cases of "catchall" patterns that may not be intended as such.
-fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
-    use PatKind::*;
-    match &*pat.kind {
-        Binding { subpattern: None, .. } => true,
-        Binding { subpattern: Some(s), .. } | Deref { subpattern: s } => pat_is_catchall(s),
-        Leaf { subpatterns: s } => s.iter().all(|p| pat_is_catchall(&p.pattern)),
+fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
+    use Constructor::*;
+    match pat.ctor() {
+        Wildcard => true,
+        Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
         _ => false,
     }
 }
@@ -428,29 +439,16 @@ macro_rules! emit_diag {
 fn check_let_reachability<'p, 'tcx>(
     cx: &mut MatchCheckCtxt<'p, 'tcx>,
     pat_id: HirId,
-    pat: &'p super::Pat<'tcx>,
+    pat: &'p DeconstructedPat<'p, 'tcx>,
     span: Span,
 ) {
     let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
-    let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
-
-    report_arm_reachability(&cx, &report, |arm_index, arm_span, arm_hir_id, _| {
-        match let_source(cx.tcx, pat_id) {
-            LetSource::IfLet | LetSource::WhileLet => {
-                match arm_index {
-                    // The arm with the user-specified pattern.
-                    0 => unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None),
-                    // The arm with the wildcard pattern.
-                    1 => irrefutable_let_pattern(cx.tcx, pat_id, arm_span),
-                    _ => bug!(),
-                }
-            }
-            LetSource::IfLetGuard if arm_index == 0 => {
-                unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None);
-            }
-            _ => {}
-        }
-    });
+    let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
+
+    // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
+    // This also reports unreachable sub-patterns though, so we can't just replace it with an
+    // `is_uninhabited` check.
+    report_arm_reachability(&cx, &report);
 
     if report.non_exhaustiveness_witnesses.is_empty() {
         // The match is exhaustive, i.e. the `if let` pattern is irrefutable.
@@ -459,18 +457,15 @@ fn check_let_reachability<'p, 'tcx>(
 }
 
 /// Report unreachable arms, if any.
-fn report_arm_reachability<'p, 'tcx, F>(
+fn report_arm_reachability<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     report: &UsefulnessReport<'p, 'tcx>,
-    unreachable: F,
-) where
-    F: Fn(usize, Span, HirId, Option<Span>),
-{
+) {
     use Reachability::*;
     let mut catchall = None;
-    for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() {
+    for (arm, is_useful) in report.arm_usefulness.iter() {
         match is_useful {
-            Unreachable => unreachable(arm_index, arm.pat.span, arm.hir_id, catchall),
+            Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall),
             Reachable(unreachables) if unreachables.is_empty() => {}
             // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
             Reachable(unreachables) => {
@@ -483,7 +478,7 @@ fn report_arm_reachability<'p, 'tcx, F>(
             }
         }
         if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
-            catchall = Some(arm.pat.span);
+            catchall = Some(arm.pat.span());
         }
     }
 }
@@ -493,7 +488,7 @@ fn non_exhaustive_match<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
     scrut_ty: Ty<'tcx>,
     sp: Span,
-    witnesses: Vec<super::Pat<'tcx>>,
+    witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
     is_empty_match: bool,
 ) {
     let non_empty_enum = match scrut_ty.kind() {
@@ -510,7 +505,7 @@ fn non_exhaustive_match<'p, 'tcx>(
             format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
         );
     } else {
-        let joined_patterns = joined_uncovered_patterns(&witnesses);
+        let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
         err = create_e0004(
             cx.tcx.sess,
             sp,
@@ -537,7 +532,7 @@ fn non_exhaustive_match<'p, 'tcx>(
     if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
         && !is_empty_match
         && witnesses.len() == 1
-        && is_wildcard(&witnesses[0])
+        && matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
     {
         err.note(&format!(
             "`{}` does not have a fixed maximum value, \
@@ -560,33 +555,40 @@ fn non_exhaustive_match<'p, 'tcx>(
     err.emit();
 }
 
-crate fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
+crate fn joined_uncovered_patterns<'p, 'tcx>(
+    cx: &MatchCheckCtxt<'p, 'tcx>,
+    witnesses: &[DeconstructedPat<'p, 'tcx>],
+) -> String {
     const LIMIT: usize = 3;
+    let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string();
     match witnesses {
         [] => bug!(),
-        [witness] => format!("`{}`", witness),
+        [witness] => format!("`{}`", witness.to_pat(cx)),
         [head @ .., tail] if head.len() < LIMIT => {
-            let head: Vec<_> = head.iter().map(<_>::to_string).collect();
-            format!("`{}` and `{}`", head.join("`, `"), tail)
+            let head: Vec<_> = head.iter().map(pat_to_str).collect();
+            format!("`{}` and `{}`", head.join("`, `"), tail.to_pat(cx))
         }
         _ => {
             let (head, tail) = witnesses.split_at(LIMIT);
-            let head: Vec<_> = head.iter().map(<_>::to_string).collect();
+            let head: Vec<_> = head.iter().map(pat_to_str).collect();
             format!("`{}` and {} more", head.join("`, `"), tail.len())
         }
     }
 }
 
-crate fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
+crate fn pattern_not_covered_label(
+    witnesses: &[DeconstructedPat<'_, '_>],
+    joined_patterns: &str,
+) -> String {
     format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
 }
 
 /// Point at the definition of non-covered `enum` variants.
-fn adt_defined_here(
-    cx: &MatchCheckCtxt<'_, '_>,
+fn adt_defined_here<'p, 'tcx>(
+    cx: &MatchCheckCtxt<'p, 'tcx>,
     err: &mut DiagnosticBuilder<'_>,
-    ty: Ty<'_>,
-    witnesses: &[super::Pat<'_>],
+    ty: Ty<'tcx>,
+    witnesses: &[DeconstructedPat<'p, 'tcx>],
 ) {
     let ty = ty.peel_refs();
     if let ty::Adt(def, _) = ty.kind() {
@@ -595,57 +597,42 @@ fn adt_defined_here(
         }
 
         if witnesses.len() < 4 {
-            for sp in maybe_point_at_variant(ty, &witnesses) {
+            for sp in maybe_point_at_variant(cx, def, witnesses.iter()) {
                 err.span_label(sp, "not covered");
             }
         }
     }
 }
 
-fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> {
+fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
+    cx: &MatchCheckCtxt<'p, 'tcx>,
+    def: &AdtDef,
+    patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
+) -> Vec<Span> {
+    use Constructor::*;
     let mut covered = vec![];
-    if let ty::Adt(def, _) = ty.kind() {
-        // Don't point at variants that have already been covered due to other patterns to avoid
-        // visual clutter.
-        for pattern in patterns {
-            use PatKind::{AscribeUserType, Deref, Leaf, Or, Variant};
-            match &*pattern.kind {
-                AscribeUserType { subpattern, .. } | Deref { subpattern } => {
-                    covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
+    for pattern in patterns {
+        if let Variant(variant_index) = pattern.ctor() {
+            if let ty::Adt(this_def, _) = pattern.ty().kind() {
+                if this_def.did != def.did {
+                    continue;
                 }
-                Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
-                    let sp = def.variants[*variant_index].ident.span;
-                    if covered.contains(&sp) {
-                        continue;
-                    }
-                    covered.push(sp);
-
-                    let pats = subpatterns
-                        .iter()
-                        .map(|field_pattern| field_pattern.pattern.clone())
-                        .collect::<Box<[_]>>();
-                    covered.extend(maybe_point_at_variant(ty, &pats));
-                }
-                Leaf { subpatterns } => {
-                    let pats = subpatterns
-                        .iter()
-                        .map(|field_pattern| field_pattern.pattern.clone())
-                        .collect::<Box<[_]>>();
-                    covered.extend(maybe_point_at_variant(ty, &pats));
-                }
-                Or { pats } => {
-                    let pats = pats.iter().cloned().collect::<Box<[_]>>();
-                    covered.extend(maybe_point_at_variant(ty, &pats));
-                }
-                _ => {}
             }
+            let sp = def.variants[*variant_index].ident.span;
+            if covered.contains(&sp) {
+                // Don't point at variants that have already been covered due to other patterns to avoid
+                // visual clutter.
+                continue;
+            }
+            covered.push(sp);
         }
+        covered.extend(maybe_point_at_variant(cx, def, pattern.iter_fields()));
     }
     covered
 }
 
 /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
-fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> bool {
+fn is_binding_by_move(cx: &MatchVisitor<'_, '_, '_>, hir_id: HirId, span: Span) -> bool {
     !cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env)
 }
 
@@ -659,7 +646,7 @@ fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> b
 /// - `x @ Some(ref mut? y)`.
 ///
 /// This analysis is *not* subsumed by NLL.
-fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
+fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pat<'_>) {
     // Extract `sub` in `binding @ sub`.
     let (name, sub) = match &pat.kind {
         hir::PatKind::Binding(.., name, Some(sub)) => (*name, sub),
index cee2a4db0a8b3b6649bf487801d361f8ff97ca6f..69a7d44ff3972894bcc7d0768e8844086db54fe9 100644 (file)
@@ -46,7 +46,7 @@
 use self::SliceKind::*;
 
 use super::compare_const_vals;
-use super::usefulness::{is_wildcard, MatchCheckCtxt, PatCtxt};
+use super::usefulness::{MatchCheckCtxt, PatCtxt};
 
 use rustc_data_structures::captures::Captures;
 use rustc_index::vec::Idx;
 use rustc_middle::mir::Field;
 use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
 use rustc_middle::ty::layout::IntegerExt;
-use rustc_middle::ty::{self, Const, Ty, TyCtxt};
+use rustc_middle::ty::{self, Const, Ty, TyCtxt, VariantDef};
 use rustc_session::lint;
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{Integer, Size, VariantIdx};
 
 use smallvec::{smallvec, SmallVec};
+use std::cell::Cell;
 use std::cmp::{self, max, min, Ordering};
+use std::fmt;
 use std::iter::{once, IntoIterator};
 use std::ops::RangeInclusive;
 
+/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
+fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
+    fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
+        if let PatKind::Or { pats } = pat.kind.as_ref() {
+            for pat in pats {
+                expand(pat, vec);
+            }
+        } else {
+            vec.push(pat)
+        }
+    }
+
+    let mut pats = Vec::new();
+    expand(pat, &mut pats);
+    pats
+}
+
 /// An inclusive interval, used for precise integer exhaustiveness checking.
 /// `IntRange`s always store a contiguous range. This means that values are
 /// encoded such that `0` encodes the minimum value for the integer,
 ///
 /// `IntRange` is never used to encode an empty range or a "range" that wraps
 /// around the (offset) space: i.e., `range.lo <= range.hi`.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, PartialEq, Eq)]
 pub(super) struct IntRange {
     range: RangeInclusive<u128>,
+    /// Keeps the bias used for encoding the range. It depends on the type of the range and
+    /// possibly the pointer size of the current architecture. The algorithm ensures we never
+    /// compare `IntRange`s with different types/architectures.
+    bias: u128,
 }
 
 impl IntRange {
@@ -131,7 +154,7 @@ fn from_const<'tcx>(
                 value.try_eval_bits(tcx, param_env, ty)
             })()?;
             let val = val ^ bias;
-            Some(IntRange { range: val..=val })
+            Some(IntRange { range: val..=val, bias })
         } else {
             None
         }
@@ -155,7 +178,7 @@ fn from_range<'tcx>(
                 // This should have been caught earlier by E0030.
                 bug!("malformed range pattern: {}..={}", lo, (hi - offset));
             }
-            Some(IntRange { range: lo..=(hi - offset) })
+            Some(IntRange { range: lo..=(hi - offset), bias })
         } else {
             None
         }
@@ -180,7 +203,7 @@ fn intersection(&self, other: &Self) -> Option<Self> {
         let (lo, hi) = self.boundaries();
         let (other_lo, other_hi) = other.boundaries();
         if lo <= other_hi && other_lo <= hi {
-            Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi) })
+            Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), bias: self.bias })
         } else {
             None
         }
@@ -203,10 +226,11 @@ fn suspicious_intersection(&self, other: &Self) -> bool {
         (lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton()
     }
 
+    /// Only used for displaying the range properly.
     fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
         let (lo, hi) = self.boundaries();
 
-        let bias = IntRange::signed_bias(tcx, ty);
+        let bias = self.bias;
         let (lo, hi) = (lo ^ bias, hi ^ bias);
 
         let env = ty::ParamEnv::empty().and(ty);
@@ -223,10 +247,10 @@ fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
     }
 
     /// Lint on likely incorrect range patterns (#63987)
-    pub(super) fn lint_overlapping_range_endpoints<'a, 'tcx: 'a>(
+    pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>(
         &self,
-        pcx: PatCtxt<'_, '_, 'tcx>,
-        ctors: impl Iterator<Item = (&'a Constructor<'tcx>, Span)>,
+        pcx: PatCtxt<'_, 'p, 'tcx>,
+        pats: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
         column_count: usize,
         hir_id: HirId,
     ) {
@@ -248,8 +272,8 @@ pub(super) fn lint_overlapping_range_endpoints<'a, 'tcx: 'a>(
             return;
         }
 
-        let overlaps: Vec<_> = ctors
-            .filter_map(|(ctor, span)| Some((ctor.as_int_range()?, span)))
+        let overlaps: Vec<_> = pats
+            .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span())))
             .filter(|(range, _)| self.suspicious_intersection(range))
             .map(|(range, span)| (self.intersection(&range).unwrap(), span))
             .collect();
@@ -291,6 +315,19 @@ fn is_covered_by(&self, other: &Self) -> bool {
     }
 }
 
+/// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and
+/// would be displayed as such. To render properly, convert to a pattern first.
+impl fmt::Debug for IntRange {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let (lo, hi) = self.boundaries();
+        let bias = self.bias;
+        let (lo, hi) = (lo ^ bias, hi ^ bias);
+        write!(f, "{}", lo)?;
+        write!(f, "{}", RangeEnd::Included)?;
+        write!(f, "{}", hi)
+    }
+}
+
 /// Represents a border between 2 integers. Because the intervals spanning borders must be able to
 /// cover every integer, we need to be able to represent 2^128 + 1 such borders.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@@ -375,13 +412,13 @@ fn iter<'a>(&'a self) -> impl Iterator<Item = IntRange> + Captures<'a> {
             // Skip duplicates.
             .filter(|(prev_border, border)| prev_border != border)
             // Finally, convert to ranges.
-            .map(|(prev_border, border)| {
+            .map(move |(prev_border, border)| {
                 let range = match (prev_border, border) {
                     (JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1),
                     (JustBefore(n), AfterMax) => n..=u128::MAX,
                     _ => unreachable!(), // Ruled out by the sorting and filtering we did
                 };
-                IntRange { range }
+                IntRange { range, bias: self.range.bias }
             })
     }
 }
@@ -389,17 +426,17 @@ fn iter<'a>(&'a self) -> impl Iterator<Item = IntRange> + Captures<'a> {
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 enum SliceKind {
     /// Patterns of length `n` (`[x, y]`).
-    FixedLen(u64),
+    FixedLen(usize),
     /// Patterns using the `..` notation (`[x, .., y]`).
     /// Captures any array constructor of `length >= i + j`.
     /// In the case where `array_len` is `Some(_)`,
     /// this indicates that we only care about the first `i` and the last `j` values of the array,
     /// and everything in between is a wildcard `_`.
-    VarLen(u64, u64),
+    VarLen(usize, usize),
 }
 
 impl SliceKind {
-    fn arity(self) -> u64 {
+    fn arity(self) -> usize {
         match self {
             FixedLen(length) => length,
             VarLen(prefix, suffix) => prefix + suffix,
@@ -407,7 +444,7 @@ fn arity(self) -> u64 {
     }
 
     /// Whether this pattern includes patterns of length `other_len`.
-    fn covers_length(self, other_len: u64) -> bool {
+    fn covers_length(self, other_len: usize) -> bool {
         match self {
             FixedLen(len) => len == other_len,
             VarLen(prefix, suffix) => prefix + suffix <= other_len,
@@ -419,13 +456,13 @@ fn covers_length(self, other_len: u64) -> bool {
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub(super) struct Slice {
     /// `None` if the matched value is a slice, `Some(n)` if it is an array of size `n`.
-    array_len: Option<u64>,
+    array_len: Option<usize>,
     /// The kind of pattern it is: fixed-length `[x, y]` or variable length `[x, .., y]`.
     kind: SliceKind,
 }
 
 impl Slice {
-    fn new(array_len: Option<u64>, kind: SliceKind) -> Self {
+    fn new(array_len: Option<usize>, kind: SliceKind) -> Self {
         let kind = match (array_len, kind) {
             // If the middle `..` is empty, we effectively have a fixed-length pattern.
             (Some(len), VarLen(prefix, suffix)) if prefix + suffix >= len => FixedLen(len),
@@ -434,7 +471,7 @@ fn new(array_len: Option<u64>, kind: SliceKind) -> Self {
         Slice { array_len, kind }
     }
 
-    fn arity(self) -> u64 {
+    fn arity(self) -> usize {
         self.kind.arity()
     }
 
@@ -508,16 +545,16 @@ fn is_covered_by(self, other: Self) -> bool {
 #[derive(Debug)]
 struct SplitVarLenSlice {
     /// If the type is an array, this is its size.
-    array_len: Option<u64>,
+    array_len: Option<usize>,
     /// The arity of the input slice.
-    arity: u64,
+    arity: usize,
     /// The smallest slice bigger than any slice seen. `max_slice.arity()` is the length `L`
     /// described above.
     max_slice: SliceKind,
 }
 
 impl SplitVarLenSlice {
-    fn new(prefix: u64, suffix: u64, array_len: Option<u64>) -> Self {
+    fn new(prefix: usize, suffix: usize, array_len: Option<usize>) -> Self {
         SplitVarLenSlice { array_len, arity: prefix + suffix, max_slice: VarLen(prefix, suffix) }
     }
 
@@ -611,6 +648,8 @@ pub(super) enum Constructor<'tcx> {
     Missing { nonexhaustive_enum_missing_real_variants: bool },
     /// Wildcard pattern.
     Wildcard,
+    /// Or-pattern.
+    Or,
 }
 
 impl<'tcx> Constructor<'tcx> {
@@ -647,60 +686,34 @@ fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx {
         }
     }
 
-    /// Determines the constructor that the given pattern can be specialized to.
-    pub(super) fn from_pat<'p>(cx: &MatchCheckCtxt<'p, 'tcx>, pat: &'p Pat<'tcx>) -> Self {
-        match pat.kind.as_ref() {
-            PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
-            PatKind::Binding { .. } | PatKind::Wild => Wildcard,
-            PatKind::Leaf { .. } | PatKind::Deref { .. } => Single,
-            &PatKind::Variant { variant_index, .. } => Variant(variant_index),
-            PatKind::Constant { value } => {
-                if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value) {
-                    IntRange(int_range)
-                } else {
-                    match pat.ty.kind() {
-                        ty::Float(_) => FloatRange(value, value, RangeEnd::Included),
-                        // In `expand_pattern`, we convert string literals to `&CONST` patterns with
-                        // `CONST` a pattern of type `str`. In truth this contains a constant of type
-                        // `&str`.
-                        ty::Str => Str(value),
-                        // All constants that can be structurally matched have already been expanded
-                        // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
-                        // opaque.
-                        _ => Opaque,
+    /// The number of fields for this constructor. This must be kept in sync with
+    /// `Fields::wildcards`.
+    pub(super) fn arity(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> usize {
+        match self {
+            Single | Variant(_) => match pcx.ty.kind() {
+                ty::Tuple(fs) => fs.len(),
+                ty::Ref(..) => 1,
+                ty::Adt(adt, ..) => {
+                    if adt.is_box() {
+                        // The only legal patterns of type `Box` (outside `std`) are `_` and box
+                        // patterns. If we're here we can assume this is a box pattern.
+                        1
+                    } else {
+                        let variant = &adt.variants[self.variant_index_for_adt(adt)];
+                        Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant).count()
                     }
                 }
-            }
-            &PatKind::Range(PatRange { lo, hi, end }) => {
-                let ty = lo.ty;
-                if let Some(int_range) = IntRange::from_range(
-                    cx.tcx,
-                    lo.eval_bits(cx.tcx, cx.param_env, lo.ty),
-                    hi.eval_bits(cx.tcx, cx.param_env, hi.ty),
-                    ty,
-                    &end,
-                ) {
-                    IntRange(int_range)
-                } else {
-                    FloatRange(lo, hi, end)
-                }
-            }
-            PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
-                let array_len = match pat.ty.kind() {
-                    ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env)),
-                    ty::Slice(_) => None,
-                    _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
-                };
-                let prefix = prefix.len() as u64;
-                let suffix = suffix.len() as u64;
-                let kind = if slice.is_some() {
-                    VarLen(prefix, suffix)
-                } else {
-                    FixedLen(prefix + suffix)
-                };
-                Slice(Slice::new(array_len, kind))
-            }
-            PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."),
+                _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx.ty),
+            },
+            Slice(slice) => slice.arity(),
+            Str(..)
+            | FloatRange(..)
+            | IntRange(..)
+            | NonExhaustive
+            | Opaque
+            | Missing { .. }
+            | Wildcard => 0,
+            Or => bug!("The `Or` constructor doesn't have a fixed arity"),
         }
     }
 
@@ -823,7 +836,7 @@ fn is_covered_by_any<'p>(
         match self {
             // If `self` is `Single`, `used_ctors` cannot contain anything else than `Single`s.
             Single => !used_ctors.is_empty(),
-            Variant(_) => used_ctors.iter().any(|c| c == self),
+            Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if i == vid)),
             IntRange(range) => used_ctors
                 .iter()
                 .filter_map(|c| c.as_int_range())
@@ -834,7 +847,7 @@ fn is_covered_by_any<'p>(
                 .any(|other| slice.is_covered_by(other)),
             // This constructor is never covered by anything else
             NonExhaustive => false,
-            Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard => {
+            Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard | Or => {
                 span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self)
             }
         }
@@ -885,7 +898,7 @@ pub(super) fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self {
         let all_ctors = match pcx.ty.kind() {
             ty::Bool => smallvec![make_range(0, 1)],
             ty::Array(sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
-                let len = len.eval_usize(cx.tcx, cx.param_env);
+                let len = len.eval_usize(cx.tcx, cx.param_env) as usize;
                 if len != 0 && cx.is_uninhabited(sub_ty) {
                     smallvec![]
                 } else {
@@ -1073,120 +1086,106 @@ pub(super) fn iter_missing<'a, 'p>(
     }
 }
 
-/// Some fields need to be explicitly hidden away in certain cases; see the comment above the
-/// `Fields` struct. This struct represents such a potentially-hidden field.
-#[derive(Debug, Copy, Clone)]
-pub(super) enum FilteredField<'p, 'tcx> {
-    Kept(&'p Pat<'tcx>),
-    Hidden,
-}
-
-impl<'p, 'tcx> FilteredField<'p, 'tcx> {
-    fn kept(self) -> Option<&'p Pat<'tcx>> {
-        match self {
-            FilteredField::Kept(p) => Some(p),
-            FilteredField::Hidden => None,
-        }
-    }
-}
-
 /// A value can be decomposed into a constructor applied to some fields. This struct represents
 /// those fields, generalized to allow patterns in each field. See also `Constructor`.
-/// This is constructed from a constructor using [`Fields::wildcards()`].
 ///
-/// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is
-/// uninhabited. For that, we filter these fields out of the matrix. This is handled automatically
-/// in `Fields`. This filtering is uncommon in practice, because uninhabited fields are rarely used,
-/// so we avoid it when possible to preserve performance.
-#[derive(Debug, Clone)]
-pub(super) enum Fields<'p, 'tcx> {
-    /// Lists of patterns that don't contain any filtered fields.
-    /// `Slice` and `Vec` behave the same; the difference is only to avoid allocating and
-    /// triple-dereferences when possible. Frankly this is premature optimization, I (Nadrieril)
-    /// have not measured if it really made a difference.
-    Slice(&'p [Pat<'tcx>]),
-    Vec(SmallVec<[&'p Pat<'tcx>; 2]>),
-    /// Patterns where some of the fields need to be hidden. For all intents and purposes we only
-    /// care about the non-hidden fields. We need to keep the real field index for those fields;
-    /// we're morally storing a `Vec<(usize, &Pat)>` but what we do is more convenient.
-    /// `len` counts the number of non-hidden fields
-    Filtered {
-        fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>,
-        len: usize,
-    },
+/// This is constructed for a constructor using [`Fields::wildcards()`]. The idea is that
+/// [`Fields::wildcards()`] constructs a list of fields where all entries are wildcards, and then
+/// given a pattern we fill some of the fields with its subpatterns.
+/// In the following example `Fields::wildcards` returns `[_, _, _, _]`. Then in
+/// `extract_pattern_arguments` we fill some of the entries, and the result is
+/// `[Some(0), _, _, _]`.
+/// ```rust
+/// let x: [Option<u8>; 4] = foo();
+/// match x {
+///     [Some(0), ..] => {}
+/// }
+/// ```
+///
+/// Note that the number of fields of a constructor may not match the fields declared in the
+/// original struct/variant. This happens if a private or `non_exhaustive` field is uninhabited,
+/// because the code mustn't observe that it is uninhabited. In that case that field is not
+/// included in `fields`. For that reason, when you have a `mir::Field` you must use
+/// `index_with_declared_idx`.
+#[derive(Debug, Clone, Copy)]
+pub(super) struct Fields<'p, 'tcx> {
+    fields: &'p [DeconstructedPat<'p, 'tcx>],
 }
 
 impl<'p, 'tcx> Fields<'p, 'tcx> {
-    /// Internal use. Use `Fields::wildcards()` instead.
-    /// Must not be used if the pattern is a field of a struct/tuple/variant.
-    fn from_single_pattern(pat: &'p Pat<'tcx>) -> Self {
-        Fields::Slice(std::slice::from_ref(pat))
+    fn empty() -> Self {
+        Fields { fields: &[] }
+    }
+
+    fn singleton(cx: &MatchCheckCtxt<'p, 'tcx>, field: DeconstructedPat<'p, 'tcx>) -> Self {
+        let field: &_ = cx.pattern_arena.alloc(field);
+        Fields { fields: std::slice::from_ref(field) }
+    }
+
+    pub(super) fn from_iter(
+        cx: &MatchCheckCtxt<'p, 'tcx>,
+        fields: impl IntoIterator<Item = DeconstructedPat<'p, 'tcx>>,
+    ) -> Self {
+        let fields: &[_] = cx.pattern_arena.alloc_from_iter(fields);
+        Fields { fields }
     }
 
-    /// Convenience; internal use.
     fn wildcards_from_tys(
         cx: &MatchCheckCtxt<'p, 'tcx>,
         tys: impl IntoIterator<Item = Ty<'tcx>>,
     ) -> Self {
-        let wilds = tys.into_iter().map(Pat::wildcard_from_ty);
-        let pats = cx.pattern_arena.alloc_from_iter(wilds);
-        Fields::Slice(pats)
+        Fields::from_iter(cx, tys.into_iter().map(DeconstructedPat::wildcard))
     }
 
-    /// Creates a new list of wildcard fields for a given constructor.
-    pub(super) fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self {
-        let ty = pcx.ty;
-        let cx = pcx.cx;
-        let wildcard_from_ty = |ty| &*cx.pattern_arena.alloc(Pat::wildcard_from_ty(ty));
+    // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide
+    // uninhabited fields in order not to reveal the uninhabitedness of the whole variant.
+    // This lists the fields we keep along with their types.
+    fn list_variant_nonhidden_fields<'a>(
+        cx: &'a MatchCheckCtxt<'p, 'tcx>,
+        ty: Ty<'tcx>,
+        variant: &'a VariantDef,
+    ) -> impl Iterator<Item = (Field, Ty<'tcx>)> + Captures<'a> + Captures<'p> {
+        let (adt, substs) = match ty.kind() {
+            ty::Adt(adt, substs) => (adt, substs),
+            _ => bug!(),
+        };
+        // Whether we must not match the fields of this variant exhaustively.
+        let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did.is_local();
+
+        variant.fields.iter().enumerate().filter_map(move |(i, field)| {
+            let ty = field.ty(cx.tcx, substs);
+            let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx);
+            let is_uninhabited = cx.is_uninhabited(ty);
+
+            if is_uninhabited && (!is_visible || is_non_exhaustive) {
+                None
+            } else {
+                Some((Field::new(i), ty))
+            }
+        })
+    }
 
+    /// Creates a new list of wildcard fields for a given constructor. The result must have a
+    /// length of `constructor.arity()`.
+    pub(super) fn wildcards(
+        cx: &MatchCheckCtxt<'p, 'tcx>,
+        ty: Ty<'tcx>,
+        constructor: &Constructor<'tcx>,
+    ) -> Self {
         let ret = match constructor {
             Single | Variant(_) => match ty.kind() {
-                ty::Tuple(ref fs) => {
-                    Fields::wildcards_from_tys(cx, fs.into_iter().map(|ty| ty.expect_ty()))
-                }
-                ty::Ref(_, rty, _) => Fields::from_single_pattern(wildcard_from_ty(rty)),
+                ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter().map(|ty| ty.expect_ty())),
+                ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)),
                 ty::Adt(adt, substs) => {
                     if adt.is_box() {
-                        // Use T as the sub pattern type of Box<T>.
-                        Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0)))
+                        // The only legal patterns of type `Box` (outside `std`) are `_` and box
+                        // patterns. If we're here we can assume this is a box pattern.
+                        Fields::wildcards_from_tys(cx, once(substs.type_at(0)))
                     } else {
                         let variant = &adt.variants[constructor.variant_index_for_adt(adt)];
-                        // Whether we must not match the fields of this variant exhaustively.
-                        let is_non_exhaustive =
-                            variant.is_field_list_non_exhaustive() && !adt.did.is_local();
-                        let field_tys = variant.fields.iter().map(|field| field.ty(cx.tcx, substs));
-                        // In the following cases, we don't need to filter out any fields. This is
-                        // the vast majority of real cases, since uninhabited fields are uncommon.
-                        let has_no_hidden_fields = (adt.is_enum() && !is_non_exhaustive)
-                            || !field_tys.clone().any(|ty| cx.is_uninhabited(ty));
-
-                        if has_no_hidden_fields {
-                            Fields::wildcards_from_tys(cx, field_tys)
-                        } else {
-                            let mut len = 0;
-                            let fields = variant
-                                .fields
-                                .iter()
-                                .map(|field| {
-                                    let ty = field.ty(cx.tcx, substs);
-                                    let is_visible = adt.is_enum()
-                                        || field.vis.is_accessible_from(cx.module, cx.tcx);
-                                    let is_uninhabited = cx.is_uninhabited(ty);
-
-                                    // In the cases of either a `#[non_exhaustive]` field list
-                                    // or a non-public field, we hide uninhabited fields in
-                                    // order not to reveal the uninhabitedness of the whole
-                                    // variant.
-                                    if is_uninhabited && (!is_visible || is_non_exhaustive) {
-                                        FilteredField::Hidden
-                                    } else {
-                                        len += 1;
-                                        FilteredField::Kept(wildcard_from_ty(ty))
-                                    }
-                                })
-                                .collect();
-                            Fields::Filtered { fields, len }
-                        }
+                        let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant)
+                            .map(|(_, ty)| ty);
+                        Fields::wildcards_from_tys(cx, tys)
                     }
                 }
                 _ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
@@ -1204,54 +1203,243 @@ pub(super) fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'t
             | NonExhaustive
             | Opaque
             | Missing { .. }
-            | Wildcard => Fields::Slice(&[]),
+            | Wildcard => Fields::empty(),
+            Or => {
+                bug!("called `Fields::wildcards` on an `Or` ctor")
+            }
         };
         debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
         ret
     }
 
-    /// Apply a constructor to a list of patterns, yielding a new pattern. `self`
-    /// must have as many elements as this constructor's arity.
-    ///
-    /// This is roughly the inverse of `specialize_constructor`.
-    ///
-    /// Examples:
-    ///
-    /// ```text
-    /// ctor: `Constructor::Single`
-    /// ty: `Foo(u32, u32, u32)`
-    /// self: `[10, 20, _]`
-    /// returns `Foo(10, 20, _)`
-    ///
-    /// ctor: `Constructor::Variant(Option::Some)`
-    /// ty: `Option<bool>`
-    /// self: `[false]`
-    /// returns `Some(false)`
-    /// ```
-    pub(super) fn apply(self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Pat<'tcx> {
-        let subpatterns_and_indices = self.patterns_and_indices();
-        let mut subpatterns = subpatterns_and_indices.iter().map(|&(_, p)| p).cloned();
-
-        let pat = match ctor {
-            Single | Variant(_) => match pcx.ty.kind() {
-                ty::Adt(..) | ty::Tuple(..) => {
-                    // We want the real indices here.
-                    let subpatterns = subpatterns_and_indices
-                        .iter()
-                        .map(|&(field, p)| FieldPat { field, pattern: p.clone() })
-                        .collect();
+    /// Returns the list of patterns.
+    pub(super) fn iter_patterns<'a>(
+        &'a self,
+    ) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'a> {
+        self.fields.iter()
+    }
+}
 
-                    if let ty::Adt(adt, substs) = pcx.ty.kind() {
-                        if adt.is_enum() {
-                            PatKind::Variant {
-                                adt_def: adt,
-                                substs,
-                                variant_index: ctor.variant_index_for_adt(adt),
-                                subpatterns,
-                            }
+/// Values and patterns can be represented as a constructor applied to some fields. This represents
+/// a pattern in this form.
+/// This also keeps track of whether the pattern has been foundreachable during analysis. For this
+/// reason we should be careful not to clone patterns for which we care about that. Use
+/// `clone_and_forget_reachability` is you're sure.
+pub(crate) struct DeconstructedPat<'p, 'tcx> {
+    ctor: Constructor<'tcx>,
+    fields: Fields<'p, 'tcx>,
+    ty: Ty<'tcx>,
+    span: Span,
+    reachable: Cell<bool>,
+}
+
+impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
+    pub(super) fn wildcard(ty: Ty<'tcx>) -> Self {
+        Self::new(Wildcard, Fields::empty(), ty, DUMMY_SP)
+    }
+
+    pub(super) fn new(
+        ctor: Constructor<'tcx>,
+        fields: Fields<'p, 'tcx>,
+        ty: Ty<'tcx>,
+        span: Span,
+    ) -> Self {
+        DeconstructedPat { ctor, fields, ty, span, reachable: Cell::new(false) }
+    }
+
+    /// Construct a pattern that matches everything that starts with this constructor.
+    /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
+    /// `Some(_)`.
+    pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self {
+        let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor);
+        DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP)
+    }
+
+    /// Clone this value. This method emphasizes that cloning loses reachability information and
+    /// should be done carefully.
+    pub(super) fn clone_and_forget_reachability(&self) -> Self {
+        DeconstructedPat::new(self.ctor.clone(), self.fields, self.ty, self.span)
+    }
+
+    pub(crate) fn from_pat(cx: &MatchCheckCtxt<'p, 'tcx>, pat: &Pat<'tcx>) -> Self {
+        let mkpat = |pat| DeconstructedPat::from_pat(cx, pat);
+        let ctor;
+        let fields;
+        match pat.kind.as_ref() {
+            PatKind::AscribeUserType { subpattern, .. } => return mkpat(subpattern),
+            PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat),
+            PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
+                ctor = Wildcard;
+                fields = Fields::empty();
+            }
+            PatKind::Deref { subpattern } => {
+                ctor = Single;
+                fields = Fields::singleton(cx, mkpat(subpattern));
+            }
+            PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
+                match pat.ty.kind() {
+                    ty::Tuple(fs) => {
+                        ctor = Single;
+                        let mut wilds: SmallVec<[_; 2]> = fs
+                            .iter()
+                            .map(|ty| ty.expect_ty())
+                            .map(DeconstructedPat::wildcard)
+                            .collect();
+                        for pat in subpatterns {
+                            wilds[pat.field.index()] = mkpat(&pat.pattern);
+                        }
+                        fields = Fields::from_iter(cx, wilds);
+                    }
+                    ty::Adt(adt, substs) if adt.is_box() => {
+                        // The only legal patterns of type `Box` (outside `std`) are `_` and box
+                        // patterns. If we're here we can assume this is a box pattern.
+                        // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
+                        // _)` or a box pattern. As a hack to avoid an ICE with the former, we
+                        // ignore other fields than the first one. This will trigger an error later
+                        // anyway.
+                        // See https://github.com/rust-lang/rust/issues/82772 ,
+                        // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977
+                        // The problem is that we can't know from the type whether we'll match
+                        // normally or through box-patterns. We'll have to figure out a proper
+                        // solution when we introduce generalized deref patterns. Also need to
+                        // prevent mixing of those two options.
+                        let pat = subpatterns.into_iter().find(|pat| pat.field.index() == 0);
+                        let pat = if let Some(pat) = pat {
+                            mkpat(&pat.pattern)
                         } else {
-                            PatKind::Leaf { subpatterns }
+                            DeconstructedPat::wildcard(substs.type_at(0))
+                        };
+                        ctor = Single;
+                        fields = Fields::singleton(cx, pat);
+                    }
+                    ty::Adt(adt, _) => {
+                        ctor = match pat.kind.as_ref() {
+                            PatKind::Leaf { .. } => Single,
+                            PatKind::Variant { variant_index, .. } => Variant(*variant_index),
+                            _ => bug!(),
+                        };
+                        let variant = &adt.variants[ctor.variant_index_for_adt(adt)];
+                        // For each field in the variant, we store the relevant index into `self.fields` if any.
+                        let mut field_id_to_id: Vec<Option<usize>> =
+                            (0..variant.fields.len()).map(|_| None).collect();
+                        let tys = Fields::list_variant_nonhidden_fields(cx, pat.ty, variant)
+                            .enumerate()
+                            .map(|(i, (field, ty))| {
+                                field_id_to_id[field.index()] = Some(i);
+                                ty
+                            });
+                        let mut wilds: SmallVec<[_; 2]> =
+                            tys.map(DeconstructedPat::wildcard).collect();
+                        for pat in subpatterns {
+                            if let Some(i) = field_id_to_id[pat.field.index()] {
+                                wilds[i] = mkpat(&pat.pattern);
+                            }
+                        }
+                        fields = Fields::from_iter(cx, wilds);
+                    }
+                    _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty),
+                }
+            }
+            PatKind::Constant { value } => {
+                if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value) {
+                    ctor = IntRange(int_range);
+                    fields = Fields::empty();
+                } else {
+                    match pat.ty.kind() {
+                        ty::Float(_) => {
+                            ctor = FloatRange(value, value, RangeEnd::Included);
+                            fields = Fields::empty();
+                        }
+                        ty::Ref(_, t, _) if t.is_str() => {
+                            // We want a `&str` constant to behave like a `Deref` pattern, to be compatible
+                            // with other `Deref` patterns. This could have been done in `const_to_pat`,
+                            // but that causes issues with the rest of the matching code.
+                            // So here, the constructor for a `"foo"` pattern is `&` (represented by
+                            // `Single`), and has one field. That field has constructor `Str(value)` and no
+                            // fields.
+                            // Note: `t` is `str`, not `&str`.
+                            let subpattern =
+                                DeconstructedPat::new(Str(value), Fields::empty(), t, pat.span);
+                            ctor = Single;
+                            fields = Fields::singleton(cx, subpattern)
+                        }
+                        // All constants that can be structurally matched have already been expanded
+                        // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
+                        // opaque.
+                        _ => {
+                            ctor = Opaque;
+                            fields = Fields::empty();
                         }
+                    }
+                }
+            }
+            &PatKind::Range(PatRange { lo, hi, end }) => {
+                let ty = lo.ty;
+                ctor = if let Some(int_range) = IntRange::from_range(
+                    cx.tcx,
+                    lo.eval_bits(cx.tcx, cx.param_env, lo.ty),
+                    hi.eval_bits(cx.tcx, cx.param_env, hi.ty),
+                    ty,
+                    &end,
+                ) {
+                    IntRange(int_range)
+                } else {
+                    FloatRange(lo, hi, end)
+                };
+                fields = Fields::empty();
+            }
+            PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
+                let array_len = match pat.ty.kind() {
+                    ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env) as usize),
+                    ty::Slice(_) => None,
+                    _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
+                };
+                let kind = if slice.is_some() {
+                    VarLen(prefix.len(), suffix.len())
+                } else {
+                    FixedLen(prefix.len() + suffix.len())
+                };
+                ctor = Slice(Slice::new(array_len, kind));
+                fields = Fields::from_iter(cx, prefix.iter().chain(suffix).map(mkpat));
+            }
+            PatKind::Or { .. } => {
+                ctor = Or;
+                let pats = expand_or_pat(pat);
+                fields = Fields::from_iter(cx, pats.into_iter().map(mkpat));
+            }
+        }
+        DeconstructedPat::new(ctor, fields, pat.ty, pat.span)
+    }
+
+    pub(crate) fn to_pat(&self, cx: &MatchCheckCtxt<'p, 'tcx>) -> Pat<'tcx> {
+        let is_wildcard = |pat: &Pat<'_>| {
+            matches!(*pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild)
+        };
+        let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx));
+        let pat = match &self.ctor {
+            Single | Variant(_) => match self.ty.kind() {
+                ty::Tuple(..) => PatKind::Leaf {
+                    subpatterns: subpatterns
+                        .enumerate()
+                        .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p })
+                        .collect(),
+                },
+                ty::Adt(adt_def, _) if adt_def.is_box() => {
+                    // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
+                    // of `std`). So this branch is only reachable when the feature is enabled and
+                    // the pattern is a box pattern.
+                    PatKind::Deref { subpattern: subpatterns.next().unwrap() }
+                }
+                ty::Adt(adt_def, substs) => {
+                    let variant_index = self.ctor.variant_index_for_adt(adt_def);
+                    let variant = &adt_def.variants[variant_index];
+                    let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty, variant)
+                        .zip(subpatterns)
+                        .map(|((field, _ty), pattern)| FieldPat { field, pattern })
+                        .collect();
+
+                    if adt_def.is_enum() {
+                        PatKind::Variant { adt_def, substs, variant_index, subpatterns }
                     } else {
                         PatKind::Leaf { subpatterns }
                     }
@@ -1259,195 +1447,239 @@ pub(super) fn apply(self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>)
                 // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
                 // be careful to reconstruct the correct constant pattern here. However a string
                 // literal pattern will never be reported as a non-exhaustiveness witness, so we
-                // can ignore this issue.
+                // ignore this issue.
                 ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
-                ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", ctor, pcx.ty),
-                _ => PatKind::Wild,
+                _ => bug!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty),
             },
-            Slice(slice) => match slice.kind {
-                FixedLen(_) => {
-                    PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] }
-                }
-                VarLen(prefix, _) => {
-                    let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix as usize).collect();
-                    if slice.array_len.is_some() {
-                        // Improves diagnostics a bit: if the type is a known-size array, instead
-                        // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
-                        // This is incorrect if the size is not known, since `[_, ..]` captures
-                        // arrays of lengths `>= 1` whereas `[..]` captures any length.
-                        while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
-                            prefix.pop();
+            Slice(slice) => {
+                match slice.kind {
+                    FixedLen(_) => PatKind::Slice {
+                        prefix: subpatterns.collect(),
+                        slice: None,
+                        suffix: vec![],
+                    },
+                    VarLen(prefix, _) => {
+                        let mut subpatterns = subpatterns.peekable();
+                        let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
+                        if slice.array_len.is_some() {
+                            // Improves diagnostics a bit: if the type is a known-size array, instead
+                            // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
+                            // This is incorrect if the size is not known, since `[_, ..]` captures
+                            // arrays of lengths `>= 1` whereas `[..]` captures any length.
+                            while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
+                                prefix.pop();
+                            }
+                            while subpatterns.peek().is_some()
+                                && is_wildcard(subpatterns.peek().unwrap())
+                            {
+                                subpatterns.next();
+                            }
                         }
+                        let suffix: Vec<_> = subpatterns.collect();
+                        let wild = Pat::wildcard_from_ty(self.ty);
+                        PatKind::Slice { prefix, slice: Some(wild), suffix }
                     }
-                    let suffix: Vec<_> = if slice.array_len.is_some() {
-                        // Same as above.
-                        subpatterns.skip_while(is_wildcard).collect()
-                    } else {
-                        subpatterns.collect()
-                    };
-                    let wild = Pat::wildcard_from_ty(pcx.ty);
-                    PatKind::Slice { prefix, slice: Some(wild), suffix }
                 }
-            },
+            }
             &Str(value) => PatKind::Constant { value },
             &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
-            IntRange(range) => return range.to_pat(pcx.cx.tcx, pcx.ty),
-            NonExhaustive => PatKind::Wild,
-            Wildcard => return Pat::wildcard_from_ty(pcx.ty),
-            Opaque => bug!("we should not try to apply an opaque constructor"),
+            IntRange(range) => return range.to_pat(cx.tcx, self.ty),
+            Wildcard | NonExhaustive => PatKind::Wild,
             Missing { .. } => bug!(
-                "trying to apply the `Missing` constructor; this should have been done in `apply_constructors`"
+                "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
+                `Missing` should have been processed in `apply_constructors`"
             ),
+            Opaque | Or => {
+                bug!("can't convert to pattern: {:?}", self)
+            }
         };
 
-        Pat { ty: pcx.ty, span: DUMMY_SP, kind: Box::new(pat) }
+        Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(pat) }
     }
 
-    /// Returns the number of patterns. This is the same as the arity of the constructor used to
-    /// construct `self`.
-    pub(super) fn len(&self) -> usize {
-        match self {
-            Fields::Slice(pats) => pats.len(),
-            Fields::Vec(pats) => pats.len(),
-            Fields::Filtered { len, .. } => *len,
-        }
+    pub(super) fn is_or_pat(&self) -> bool {
+        matches!(self.ctor, Or)
     }
 
-    /// Returns the list of patterns along with the corresponding field indices.
-    fn patterns_and_indices(&self) -> SmallVec<[(Field, &'p Pat<'tcx>); 2]> {
-        match self {
-            Fields::Slice(pats) => {
-                pats.iter().enumerate().map(|(i, p)| (Field::new(i), p)).collect()
-            }
-            Fields::Vec(pats) => {
-                pats.iter().copied().enumerate().map(|(i, p)| (Field::new(i), p)).collect()
-            }
-            Fields::Filtered { fields, .. } => {
-                // Indices must be relative to the full list of patterns
-                fields
-                    .iter()
-                    .enumerate()
-                    .filter_map(|(i, p)| Some((Field::new(i), p.kept()?)))
-                    .collect()
-            }
-        }
+    pub(super) fn ctor(&self) -> &Constructor<'tcx> {
+        &self.ctor
     }
-
-    /// Returns the list of patterns.
-    pub(super) fn into_patterns(self) -> SmallVec<[&'p Pat<'tcx>; 2]> {
-        match self {
-            Fields::Slice(pats) => pats.iter().collect(),
-            Fields::Vec(pats) => pats,
-            Fields::Filtered { fields, .. } => fields.iter().filter_map(|p| p.kept()).collect(),
-        }
+    pub(super) fn ty(&self) -> Ty<'tcx> {
+        self.ty
     }
-
-    /// Overrides some of the fields with the provided patterns. Exactly like
-    /// `replace_fields_indexed`, except that it takes `FieldPat`s as input.
-    fn replace_with_fieldpats(
-        &self,
-        new_pats: impl IntoIterator<Item = &'p FieldPat<'tcx>>,
-    ) -> Self {
-        self.replace_fields_indexed(
-            new_pats.into_iter().map(|pat| (pat.field.index(), &pat.pattern)),
-        )
+    pub(super) fn span(&self) -> Span {
+        self.span
     }
 
-    /// Overrides some of the fields with the provided patterns. This is used when a pattern
-    /// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start
-    /// with a `Fields` that is just one wildcard per field of the `Foo` struct, and override the
-    /// entry corresponding to `field1` with the pattern `Some(_)`. This is also used for slice
-    /// patterns for the same reason.
-    fn replace_fields_indexed(
-        &self,
-        new_pats: impl IntoIterator<Item = (usize, &'p Pat<'tcx>)>,
-    ) -> Self {
-        let mut fields = self.clone();
-        if let Fields::Slice(pats) = fields {
-            fields = Fields::Vec(pats.iter().collect());
-        }
+    pub(super) fn iter_fields<'a>(
+        &'a self,
+    ) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'a> {
+        self.fields.iter_patterns()
+    }
 
-        match &mut fields {
-            Fields::Vec(pats) => {
-                for (i, pat) in new_pats {
-                    if let Some(p) = pats.get_mut(i) {
-                        *p = pat;
-                    }
-                }
+    /// Specialize this pattern with a constructor.
+    /// `other_ctor` can be different from `self.ctor`, but must be covered by it.
+    pub(super) fn specialize<'a>(
+        &'a self,
+        cx: &MatchCheckCtxt<'p, 'tcx>,
+        other_ctor: &Constructor<'tcx>,
+    ) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> {
+        match (&self.ctor, other_ctor) {
+            (Wildcard, _) => {
+                // We return a wildcard for each field of `other_ctor`.
+                Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect()
             }
-            Fields::Filtered { fields, .. } => {
-                for (i, pat) in new_pats {
-                    if let FilteredField::Kept(p) = &mut fields[i] {
-                        *p = pat
+            (Slice(self_slice), Slice(other_slice))
+                if self_slice.arity() != other_slice.arity() =>
+            {
+                // The only tricky case: two slices of different arity. Since `self_slice` covers
+                // `other_slice`, `self_slice` must be `VarLen`, i.e. of the form
+                // `[prefix, .., suffix]`. Moreover `other_slice` is guaranteed to have a larger
+                // arity. So we fill the middle part with enough wildcards to reach the length of
+                // the new, larger slice.
+                match self_slice.kind {
+                    FixedLen(_) => bug!("{:?} doesn't cover {:?}", self_slice, other_slice),
+                    VarLen(prefix, suffix) => {
+                        let inner_ty = match *self.ty.kind() {
+                            ty::Slice(ty) | ty::Array(ty, _) => ty,
+                            _ => bug!("bad slice pattern {:?} {:?}", self.ctor, self.ty),
+                        };
+                        let prefix = &self.fields.fields[..prefix];
+                        let suffix = &self.fields.fields[self_slice.arity() - suffix..];
+                        let wildcard: &_ =
+                            cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty));
+                        let extra_wildcards = other_slice.arity() - self_slice.arity();
+                        let extra_wildcards = (0..extra_wildcards).map(|_| wildcard);
+                        prefix.iter().chain(extra_wildcards).chain(suffix).collect()
                     }
                 }
             }
-            Fields::Slice(_) => unreachable!(),
+            _ => self.fields.iter_patterns().collect(),
         }
-        fields
     }
 
-    /// Replaces contained fields with the given list of patterns. There must be `len()` patterns
-    /// in `pats`.
-    pub(super) fn replace_fields(
-        &self,
-        cx: &MatchCheckCtxt<'p, 'tcx>,
-        pats: impl IntoIterator<Item = Pat<'tcx>>,
-    ) -> Self {
-        let pats: &[_] = cx.pattern_arena.alloc_from_iter(pats);
+    /// We keep track for each pattern if it was ever reachable during the analysis. This is used
+    /// with `unreachable_spans` to report unreachable subpatterns arising from or patterns.
+    pub(super) fn set_reachable(&self) {
+        self.reachable.set(true)
+    }
+    pub(super) fn is_reachable(&self) -> bool {
+        self.reachable.get()
+    }
 
-        match self {
-            Fields::Filtered { fields, len } => {
-                let mut pats = pats.iter();
-                let mut fields = fields.clone();
-                for f in &mut fields {
-                    if let FilteredField::Kept(p) = f {
-                        // We take one input pattern for each `Kept` field, in order.
-                        *p = pats.next().unwrap();
-                    }
-                }
-                Fields::Filtered { fields, len: *len }
+    /// Report the spans of subpatterns that were not reachable, if any.
+    pub(super) fn unreachable_spans(&self) -> Vec<Span> {
+        let mut spans = Vec::new();
+        self.collect_unreachable_spans(&mut spans);
+        spans
+    }
+
+    fn collect_unreachable_spans(&self, spans: &mut Vec<Span>) {
+        // We don't look at subpatterns if we already reported the whole pattern as unreachable.
+        if !self.is_reachable() {
+            spans.push(self.span);
+        } else {
+            for p in self.iter_fields() {
+                p.collect_unreachable_spans(spans);
             }
-            _ => Fields::Slice(pats),
         }
     }
+}
 
-    /// Replaces contained fields with the arguments of the given pattern. Only use on a pattern
-    /// that is compatible with the constructor used to build `self`.
-    /// This is meant to be used on the result of `Fields::wildcards()`. The idea is that
-    /// `wildcards` constructs a list of fields where all entries are wildcards, and the pattern
-    /// provided to this function fills some of the fields with non-wildcards.
-    /// In the following example `Fields::wildcards` would return `[_, _, _, _]`. If we call
-    /// `replace_with_pattern_arguments` on it with the pattern, the result will be `[Some(0), _,
-    /// _, _]`.
-    /// ```rust
-    /// let x: [Option<u8>; 4] = foo();
-    /// match x {
-    ///     [Some(0), ..] => {}
-    /// }
-    /// ```
-    /// This is guaranteed to preserve the number of patterns in `self`.
-    pub(super) fn replace_with_pattern_arguments(&self, pat: &'p Pat<'tcx>) -> Self {
-        match pat.kind.as_ref() {
-            PatKind::Deref { subpattern } => {
-                assert_eq!(self.len(), 1);
-                Fields::from_single_pattern(subpattern)
+/// This is mostly copied from the `Pat` impl. This is best effort and not good enough for a
+/// `Display` impl.
+impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // Printing lists is a chore.
+        let mut first = true;
+        let mut start_or_continue = |s| {
+            if first {
+                first = false;
+                ""
+            } else {
+                s
             }
-            PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
-                self.replace_with_fieldpats(subpatterns)
+        };
+        let mut start_or_comma = || start_or_continue(", ");
+
+        match &self.ctor {
+            Single | Variant(_) => match self.ty.kind() {
+                ty::Adt(def, _) if def.is_box() => {
+                    // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
+                    // of `std`). So this branch is only reachable when the feature is enabled and
+                    // the pattern is a box pattern.
+                    let subpattern = self.iter_fields().next().unwrap();
+                    write!(f, "box {:?}", subpattern)
+                }
+                ty::Adt(..) | ty::Tuple(..) => {
+                    let variant = match self.ty.kind() {
+                        ty::Adt(adt, _) => {
+                            Some(&adt.variants[self.ctor.variant_index_for_adt(adt)])
+                        }
+                        ty::Tuple(_) => None,
+                        _ => unreachable!(),
+                    };
+
+                    if let Some(variant) = variant {
+                        write!(f, "{}", variant.ident)?;
+                    }
+
+                    // Without `cx`, we can't know which field corresponds to which, so we can't
+                    // get the names of the fields. Instead we just display everything as a suple
+                    // struct, which should be good enough.
+                    write!(f, "(")?;
+                    for p in self.iter_fields() {
+                        write!(f, "{}", start_or_comma())?;
+                        write!(f, "{:?}", p)?;
+                    }
+                    write!(f, ")")
+                }
+                // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
+                // be careful to detect strings here. However a string literal pattern will never
+                // be reported as a non-exhaustiveness witness, so we can ignore this issue.
+                ty::Ref(_, _, mutbl) => {
+                    let subpattern = self.iter_fields().next().unwrap();
+                    write!(f, "&{}{:?}", mutbl.prefix_str(), subpattern)
+                }
+                _ => write!(f, "_"),
+            },
+            Slice(slice) => {
+                let mut subpatterns = self.fields.iter_patterns();
+                write!(f, "[")?;
+                match slice.kind {
+                    FixedLen(_) => {
+                        for p in subpatterns {
+                            write!(f, "{}{:?}", start_or_comma(), p)?;
+                        }
+                    }
+                    VarLen(prefix_len, _) => {
+                        for p in subpatterns.by_ref().take(prefix_len) {
+                            write!(f, "{}{:?}", start_or_comma(), p)?;
+                        }
+                        write!(f, "{}", start_or_comma())?;
+                        write!(f, "..")?;
+                        for p in subpatterns {
+                            write!(f, "{}{:?}", start_or_comma(), p)?;
+                        }
+                    }
+                }
+                write!(f, "]")
+            }
+            &FloatRange(lo, hi, end) => {
+                write!(f, "{}", lo)?;
+                write!(f, "{}", end)?;
+                write!(f, "{}", hi)
             }
-            PatKind::Array { prefix, suffix, .. } | PatKind::Slice { prefix, suffix, .. } => {
-                // Number of subpatterns for the constructor
-                let ctor_arity = self.len();
-
-                // Replace the prefix and the suffix with the given patterns, leaving wildcards in
-                // the middle if there was a subslice pattern `..`.
-                let prefix = prefix.iter().enumerate();
-                let suffix =
-                    suffix.iter().enumerate().map(|(i, p)| (ctor_arity - suffix.len() + i, p));
-                self.replace_fields_indexed(prefix.chain(suffix))
+            IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render e.g. `false` as `0..=0`
+            Wildcard | Missing { .. } | NonExhaustive => write!(f, "_"),
+            Or => {
+                for pat in self.iter_fields() {
+                    write!(f, "{}{:?}", start_or_continue(" | "), pat)?;
+                }
+                Ok(())
             }
-            _ => self.clone(),
+            Str(value) => write!(f, "{}", value),
+            Opaque => write!(f, "<constant pattern>"),
         }
     }
 }
index f4255713e2a378bebca4e2c010c5c7b9447565c6..650a87b2d885976e8a9b04a0e9492e58c52e0069 100644 (file)
 use self::Usefulness::*;
 
 use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label};
-use super::deconstruct_pat::{Constructor, Fields, SplitWildcard};
-use super::{PatternFoldable, PatternFolder};
+use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard};
 
 use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::FxHashMap;
 
-use hir::def_id::DefId;
-use hir::HirId;
 use rustc_arena::TypedArena;
-use rustc_hir as hir;
-use rustc_middle::thir::{Pat, PatKind};
+use rustc_hir::def_id::DefId;
+use rustc_hir::HirId;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
 
 use smallvec::{smallvec, SmallVec};
 use std::fmt;
-use std::iter::{FromIterator, IntoIterator};
-use std::lazy::OnceCell;
+use std::iter::once;
 
-crate struct MatchCheckCtxt<'a, 'tcx> {
+crate struct MatchCheckCtxt<'p, 'tcx> {
     crate tcx: TyCtxt<'tcx>,
     /// The module in which the match occurs. This is necessary for
     /// checking inhabited-ness of types because whether a type is (visibly)
     /// outside its module and should not be matchable with an empty match statement.
     crate module: DefId,
     crate param_env: ty::ParamEnv<'tcx>,
-    crate pattern_arena: &'a TypedArena<Pat<'tcx>>,
+    crate pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
 }
 
 impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
@@ -356,78 +351,20 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-crate fn expand_pattern<'tcx>(pat: Pat<'tcx>) -> Pat<'tcx> {
-    LiteralExpander.fold_pattern(&pat)
-}
-
-struct LiteralExpander;
-
-impl<'tcx> PatternFolder<'tcx> for LiteralExpander {
-    fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> {
-        debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind);
-        match (pat.ty.kind(), pat.kind.as_ref()) {
-            (_, PatKind::Binding { subpattern: Some(s), .. }) => s.fold_with(self),
-            (_, PatKind::AscribeUserType { subpattern: s, .. }) => s.fold_with(self),
-            (ty::Ref(_, t, _), PatKind::Constant { .. }) if t.is_str() => {
-                // Treat string literal patterns as deref patterns to a `str` constant, i.e.
-                // `&CONST`. This expands them like other const patterns. This could have been done
-                // in `const_to_pat`, but that causes issues with the rest of the matching code.
-                let mut new_pat = pat.super_fold_with(self);
-                // Make a fake const pattern of type `str` (instead of `&str`). That the carried
-                // constant value still knows it is of type `&str`.
-                new_pat.ty = t;
-                Pat {
-                    kind: Box::new(PatKind::Deref { subpattern: new_pat }),
-                    span: pat.span,
-                    ty: pat.ty,
-                }
-            }
-            _ => pat.super_fold_with(self),
-        }
-    }
-}
-
-pub(super) fn is_wildcard(pat: &Pat<'_>) -> bool {
-    matches!(*pat.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild)
-}
-
-fn is_or_pat(pat: &Pat<'_>) -> bool {
-    matches!(*pat.kind, PatKind::Or { .. })
-}
-
-/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
-fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
-    fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
-        if let PatKind::Or { pats } = pat.kind.as_ref() {
-            for pat in pats {
-                expand(pat, vec);
-            }
-        } else {
-            vec.push(pat)
-        }
-    }
-
-    let mut pats = Vec::new();
-    expand(pat, &mut pats);
-    pats
-}
-
 /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
 /// works well.
 #[derive(Clone)]
 struct PatStack<'p, 'tcx> {
-    pats: SmallVec<[&'p Pat<'tcx>; 2]>,
-    /// Cache for the constructor of the head
-    head_ctor: OnceCell<Constructor<'tcx>>,
+    pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>,
 }
 
 impl<'p, 'tcx> PatStack<'p, 'tcx> {
-    fn from_pattern(pat: &'p Pat<'tcx>) -> Self {
+    fn from_pattern(pat: &'p DeconstructedPat<'p, 'tcx>) -> Self {
         Self::from_vec(smallvec![pat])
     }
 
-    fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self {
-        PatStack { pats: vec, head_ctor: OnceCell::new() }
+    fn from_vec(vec: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>) -> Self {
+        PatStack { pats: vec }
     }
 
     fn is_empty(&self) -> bool {
@@ -438,79 +375,56 @@ fn len(&self) -> usize {
         self.pats.len()
     }
 
-    fn head(&self) -> &'p Pat<'tcx> {
+    fn head(&self) -> &'p DeconstructedPat<'p, 'tcx> {
         self.pats[0]
     }
 
-    #[inline]
-    fn head_ctor<'a>(&'a self, cx: &MatchCheckCtxt<'p, 'tcx>) -> &'a Constructor<'tcx> {
-        self.head_ctor.get_or_init(|| Constructor::from_pat(cx, self.head()))
-    }
-
-    fn iter(&self) -> impl Iterator<Item = &Pat<'tcx>> {
+    fn iter(&self) -> impl Iterator<Item = &DeconstructedPat<'p, 'tcx>> {
         self.pats.iter().copied()
     }
 
     // Recursively expand the first pattern into its subpatterns. Only useful if the pattern is an
     // or-pattern. Panics if `self` is empty.
     fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> {
-        expand_or_pat(self.head()).into_iter().map(move |pat| {
+        self.head().iter_fields().map(move |pat| {
             let mut new_patstack = PatStack::from_pattern(pat);
             new_patstack.pats.extend_from_slice(&self.pats[1..]);
             new_patstack
         })
     }
 
-    /// This computes `S(self.head_ctor(), self)`. See top of the file for explanations.
+    /// This computes `S(self.head().ctor(), self)`. See top of the file for explanations.
     ///
     /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
     /// fields filled with wild patterns.
     ///
     /// This is roughly the inverse of `Constructor::apply`.
-    fn pop_head_constructor(&self, ctor_wild_subpatterns: &Fields<'p, 'tcx>) -> PatStack<'p, 'tcx> {
+    fn pop_head_constructor(
+        &self,
+        cx: &MatchCheckCtxt<'p, 'tcx>,
+        ctor: &Constructor<'tcx>,
+    ) -> PatStack<'p, 'tcx> {
         // We pop the head pattern and push the new fields extracted from the arguments of
         // `self.head()`.
-        let mut new_fields =
-            ctor_wild_subpatterns.replace_with_pattern_arguments(self.head()).into_patterns();
+        let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor);
         new_fields.extend_from_slice(&self.pats[1..]);
         PatStack::from_vec(new_fields)
     }
 }
 
-impl<'p, 'tcx> Default for PatStack<'p, 'tcx> {
-    fn default() -> Self {
-        Self::from_vec(smallvec![])
-    }
-}
-
-impl<'p, 'tcx> PartialEq for PatStack<'p, 'tcx> {
-    fn eq(&self, other: &Self) -> bool {
-        self.pats == other.pats
-    }
-}
-
-impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
-    fn from_iter<T>(iter: T) -> Self
-    where
-        T: IntoIterator<Item = &'p Pat<'tcx>>,
-    {
-        Self::from_vec(iter.into_iter().collect())
-    }
-}
-
 /// Pretty-printing for matrix row.
 impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "+")?;
         for pat in self.iter() {
-            write!(f, " {} +", pat)?;
+            write!(f, " {:?} +", pat)?;
         }
         Ok(())
     }
 }
 
 /// A 2D matrix.
-#[derive(Clone, PartialEq)]
+#[derive(Clone)]
 pub(super) struct Matrix<'p, 'tcx> {
     patterns: Vec<PatStack<'p, 'tcx>>,
 }
@@ -528,7 +442,7 @@ pub(super) fn column_count(&self) -> Option<usize> {
     /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
     /// expands it.
     fn push(&mut self, row: PatStack<'p, 'tcx>) {
-        if !row.is_empty() && is_or_pat(row.head()) {
+        if !row.is_empty() && row.head().is_or_pat() {
             for row in row.expand_or_pat() {
                 self.patterns.push(row);
             }
@@ -538,24 +452,10 @@ fn push(&mut self, row: PatStack<'p, 'tcx>) {
     }
 
     /// Iterate over the first component of each row
-    fn heads<'a>(&'a self) -> impl Iterator<Item = &'a Pat<'tcx>> + Captures<'p> {
-        self.patterns.iter().map(|r| r.head())
-    }
-
-    /// Iterate over the first constructor of each row.
-    pub(super) fn head_ctors<'a>(
+    fn heads<'a>(
         &'a self,
-        cx: &'a MatchCheckCtxt<'p, 'tcx>,
-    ) -> impl Iterator<Item = &'a Constructor<'tcx>> + Captures<'p> + Clone {
-        self.patterns.iter().map(move |r| r.head_ctor(cx))
-    }
-
-    /// Iterate over the first constructor and the corresponding span of each row.
-    pub(super) fn head_ctors_and_spans<'a>(
-        &'a self,
-        cx: &'a MatchCheckCtxt<'p, 'tcx>,
-    ) -> impl Iterator<Item = (&'a Constructor<'tcx>, Span)> + Captures<'p> {
-        self.patterns.iter().map(move |r| (r.head_ctor(cx), r.head().span))
+    ) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Clone + Captures<'a> {
+        self.patterns.iter().map(|r| r.head())
     }
 
     /// This computes `S(constructor, self)`. See top of the file for explanations.
@@ -563,13 +463,15 @@ fn specialize_constructor(
         &self,
         pcx: PatCtxt<'_, 'p, 'tcx>,
         ctor: &Constructor<'tcx>,
-        ctor_wild_subpatterns: &Fields<'p, 'tcx>,
     ) -> Matrix<'p, 'tcx> {
-        self.patterns
-            .iter()
-            .filter(|r| ctor.is_covered_by(pcx, r.head_ctor(pcx.cx)))
-            .map(|r| r.pop_head_constructor(ctor_wild_subpatterns))
-            .collect()
+        let mut matrix = Matrix::empty();
+        for row in &self.patterns {
+            if ctor.is_covered_by(pcx, row.head().ctor()) {
+                let new_row = row.pop_head_constructor(pcx.cx, ctor);
+                matrix.push(new_row);
+            }
+        }
+        matrix
     }
 }
 
@@ -588,7 +490,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
         let Matrix { patterns: m, .. } = self;
         let pretty_printed_matrix: Vec<Vec<String>> =
-            m.iter().map(|row| row.iter().map(|pat| format!("{}", pat)).collect()).collect();
+            m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect();
 
         let column_count = m.iter().map(|row| row.len()).next().unwrap_or(0);
         assert!(m.iter().all(|row| row.len() == column_count));
@@ -609,296 +511,40 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-impl<'p, 'tcx> FromIterator<PatStack<'p, 'tcx>> for Matrix<'p, 'tcx> {
-    fn from_iter<T>(iter: T) -> Self
-    where
-        T: IntoIterator<Item = PatStack<'p, 'tcx>>,
-    {
-        let mut matrix = Matrix::empty();
-        for x in iter {
-            // Using `push` ensures we correctly expand or-patterns.
-            matrix.push(x);
-        }
-        matrix
-    }
-}
-
-/// Given a pattern or a pattern-stack, this struct captures a set of its subpatterns. We use that
-/// to track reachable sub-patterns arising from or-patterns. In the absence of or-patterns this
-/// will always be either `Empty` (the whole pattern is unreachable) or `Full` (the whole pattern
-/// is reachable). When there are or-patterns, some subpatterns may be reachable while others
-/// aren't. In this case the whole pattern still counts as reachable, but we will lint the
-/// unreachable subpatterns.
-///
-/// This supports a limited set of operations, so not all possible sets of subpatterns can be
-/// represented. That's ok, we only want the ones that make sense for our usage.
-///
-/// What we're doing is illustrated by this:
-/// ```
-/// match (true, 0) {
-///     (true, 0) => {}
-///     (_, 1) => {}
-///     (true | false, 0 | 1) => {}
-/// }
-/// ```
-/// When we try the alternatives of the `true | false` or-pattern, the last `0` is reachable in the
-/// `false` alternative but not the `true`. So overall it is reachable. By contrast, the last `1`
-/// is not reachable in either alternative, so we want to signal this to the user.
-/// Therefore we take the union of sets of reachable patterns coming from different alternatives in
-/// order to figure out which subpatterns are overall reachable.
-///
-/// Invariant: we try to construct the smallest representation we can. In particular if
-/// `self.is_empty()` we ensure that `self` is `Empty`, and same with `Full`. This is not important
-/// for correctness currently.
-#[derive(Debug, Clone)]
-enum SubPatSet<'p, 'tcx> {
-    /// The empty set. This means the pattern is unreachable.
-    Empty,
-    /// The set containing the full pattern.
-    Full,
-    /// If the pattern is a pattern with a constructor or a pattern-stack, we store a set for each
-    /// of its subpatterns. Missing entries in the map are implicitly full, because that's the
-    /// common case.
-    Seq { subpats: FxHashMap<usize, SubPatSet<'p, 'tcx>> },
-    /// If the pattern is an or-pattern, we store a set for each of its alternatives. Missing
-    /// entries in the map are implicitly empty. Note: we always flatten nested or-patterns.
-    Alt {
-        subpats: FxHashMap<usize, SubPatSet<'p, 'tcx>>,
-        /// Counts the total number of alternatives in the pattern
-        alt_count: usize,
-        /// We keep the pattern around to retrieve spans.
-        pat: &'p Pat<'tcx>,
-    },
-}
-
-impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
-    fn full() -> Self {
-        SubPatSet::Full
-    }
-    fn empty() -> Self {
-        SubPatSet::Empty
-    }
-
-    fn is_empty(&self) -> bool {
-        match self {
-            SubPatSet::Empty => true,
-            SubPatSet::Full => false,
-            // If any subpattern in a sequence is unreachable, the whole pattern is unreachable.
-            SubPatSet::Seq { subpats } => subpats.values().any(|set| set.is_empty()),
-            // An or-pattern is reachable if any of its alternatives is.
-            SubPatSet::Alt { subpats, .. } => subpats.values().all(|set| set.is_empty()),
-        }
-    }
-
-    fn is_full(&self) -> bool {
-        match self {
-            SubPatSet::Empty => false,
-            SubPatSet::Full => true,
-            // The whole pattern is reachable only when all its alternatives are.
-            SubPatSet::Seq { subpats } => subpats.values().all(|sub_set| sub_set.is_full()),
-            // The whole or-pattern is reachable only when all its alternatives are.
-            SubPatSet::Alt { subpats, alt_count, .. } => {
-                subpats.len() == *alt_count && subpats.values().all(|set| set.is_full())
-            }
-        }
-    }
-
-    /// Union `self` with `other`, mutating `self`.
-    fn union(&mut self, other: Self) {
-        use SubPatSet::*;
-        // Union with full stays full; union with empty changes nothing.
-        if self.is_full() || other.is_empty() {
-            return;
-        } else if self.is_empty() {
-            *self = other;
-            return;
-        } else if other.is_full() {
-            *self = Full;
-            return;
-        }
-
-        match (&mut *self, other) {
-            (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => {
-                s_set.retain(|i, s_sub_set| {
-                    // Missing entries count as full.
-                    let o_sub_set = o_set.remove(&i).unwrap_or(Full);
-                    s_sub_set.union(o_sub_set);
-                    // We drop full entries.
-                    !s_sub_set.is_full()
-                });
-                // Everything left in `o_set` is missing from `s_set`, i.e. counts as full. Since
-                // unioning with full returns full, we can drop those entries.
-            }
-            (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => {
-                s_set.retain(|i, s_sub_set| {
-                    // Missing entries count as empty.
-                    let o_sub_set = o_set.remove(&i).unwrap_or(Empty);
-                    s_sub_set.union(o_sub_set);
-                    // We drop empty entries.
-                    !s_sub_set.is_empty()
-                });
-                // Everything left in `o_set` is missing from `s_set`, i.e. counts as empty. Since
-                // unioning with empty changes nothing, we can take those entries as is.
-                s_set.extend(o_set);
-            }
-            _ => bug!(),
-        }
-
-        if self.is_full() {
-            *self = Full;
-        }
-    }
-
-    /// Returns a list of the spans of the unreachable subpatterns. If `self` is empty (i.e. the
-    /// whole pattern is unreachable) we return `None`.
-    fn list_unreachable_spans(&self) -> Option<Vec<Span>> {
-        /// Panics if `set.is_empty()`.
-        fn fill_spans(set: &SubPatSet<'_, '_>, spans: &mut Vec<Span>) {
-            match set {
-                SubPatSet::Empty => bug!(),
-                SubPatSet::Full => {}
-                SubPatSet::Seq { subpats } => {
-                    for (_, sub_set) in subpats {
-                        fill_spans(sub_set, spans);
-                    }
-                }
-                SubPatSet::Alt { subpats, pat, alt_count, .. } => {
-                    let expanded = expand_or_pat(pat);
-                    for i in 0..*alt_count {
-                        let sub_set = subpats.get(&i).unwrap_or(&SubPatSet::Empty);
-                        if sub_set.is_empty() {
-                            // Found an unreachable subpattern.
-                            spans.push(expanded[i].span);
-                        } else {
-                            fill_spans(sub_set, spans);
-                        }
-                    }
-                }
-            }
-        }
-
-        if self.is_empty() {
-            return None;
-        }
-        if self.is_full() {
-            // No subpatterns are unreachable.
-            return Some(Vec::new());
-        }
-        let mut spans = Vec::new();
-        fill_spans(self, &mut spans);
-        Some(spans)
-    }
-
-    /// When `self` refers to a patstack that was obtained from specialization, after running
-    /// `unspecialize` it will refer to the original patstack before specialization.
-    fn unspecialize(self, arity: usize) -> Self {
-        use SubPatSet::*;
-        match self {
-            Full => Full,
-            Empty => Empty,
-            Seq { subpats } => {
-                // We gather the first `arity` subpatterns together and shift the remaining ones.
-                let mut new_subpats = FxHashMap::default();
-                let mut new_subpats_first_col = FxHashMap::default();
-                for (i, sub_set) in subpats {
-                    if i < arity {
-                        // The first `arity` indices are now part of the pattern in the first
-                        // column.
-                        new_subpats_first_col.insert(i, sub_set);
-                    } else {
-                        // Indices after `arity` are simply shifted
-                        new_subpats.insert(i - arity + 1, sub_set);
-                    }
-                }
-                // If `new_subpats_first_col` has no entries it counts as full, so we can omit it.
-                if !new_subpats_first_col.is_empty() {
-                    new_subpats.insert(0, Seq { subpats: new_subpats_first_col });
-                }
-                Seq { subpats: new_subpats }
-            }
-            Alt { .. } => bug!(), // `self` is a patstack
-        }
-    }
-
-    /// When `self` refers to a patstack that was obtained from splitting an or-pattern, after
-    /// running `unspecialize` it will refer to the original patstack before splitting.
-    ///
-    /// For example:
-    /// ```
-    /// match Some(true) {
-    ///     Some(true) => {}
-    ///     None | Some(true | false) => {}
-    /// }
-    /// ```
-    /// Here `None` would return the full set and `Some(true | false)` would return the set
-    /// containing `false`. After `unsplit_or_pat`, we want the set to contain `None` and `false`.
-    /// This is what this function does.
-    fn unsplit_or_pat(mut self, alt_id: usize, alt_count: usize, pat: &'p Pat<'tcx>) -> Self {
-        use SubPatSet::*;
-        if self.is_empty() {
-            return Empty;
-        }
-
-        // Subpatterns coming from inside the or-pattern alternative itself, e.g. in `None | Some(0
-        // | 1)`.
-        let set_first_col = match &mut self {
-            Full => Full,
-            Seq { subpats } => subpats.remove(&0).unwrap_or(Full),
-            Empty => unreachable!(),
-            Alt { .. } => bug!(), // `self` is a patstack
-        };
-        let mut subpats_first_col = FxHashMap::default();
-        subpats_first_col.insert(alt_id, set_first_col);
-        let set_first_col = Alt { subpats: subpats_first_col, pat, alt_count };
-
-        let mut subpats = match self {
-            Full => FxHashMap::default(),
-            Seq { subpats } => subpats,
-            Empty => unreachable!(),
-            Alt { .. } => bug!(), // `self` is a patstack
-        };
-        subpats.insert(0, set_first_col);
-        Seq { subpats }
-    }
-}
-
 /// This carries the results of computing usefulness, as described at the top of the file. When
 /// checking usefulness of a match branch, we use the `NoWitnesses` variant, which also keeps track
 /// of potential unreachable sub-patterns (in the presence of or-patterns). When checking
 /// exhaustiveness of a whole match, we use the `WithWitnesses` variant, which carries a list of
 /// witnesses of non-exhaustiveness when there are any.
 /// Which variant to use is dictated by `ArmType`.
-#[derive(Clone, Debug)]
+#[derive(Debug)]
 enum Usefulness<'p, 'tcx> {
-    /// Carries a set of subpatterns that have been found to be reachable. If empty, this indicates
-    /// the whole pattern is unreachable. If not, this indicates that the pattern is reachable but
-    /// that some sub-patterns may be unreachable (due to or-patterns). In the absence of
-    /// or-patterns this will always be either `Empty` (the whole pattern is unreachable) or `Full`
-    /// (the whole pattern is reachable).
-    NoWitnesses(SubPatSet<'p, 'tcx>),
+    /// If we don't care about witnesses, simply remember if the pattern was useful.
+    NoWitnesses { useful: bool },
     /// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole
     /// pattern is unreachable.
-    WithWitnesses(Vec<Witness<'tcx>>),
+    WithWitnesses(Vec<Witness<'p, 'tcx>>),
 }
 
 impl<'p, 'tcx> Usefulness<'p, 'tcx> {
     fn new_useful(preference: ArmType) -> Self {
         match preference {
+            // A single (empty) witness of reachability.
             FakeExtraWildcard => WithWitnesses(vec![Witness(vec![])]),
-            RealArm => NoWitnesses(SubPatSet::full()),
+            RealArm => NoWitnesses { useful: true },
         }
     }
 
     fn new_not_useful(preference: ArmType) -> Self {
         match preference {
             FakeExtraWildcard => WithWitnesses(vec![]),
-            RealArm => NoWitnesses(SubPatSet::empty()),
+            RealArm => NoWitnesses { useful: false },
         }
     }
 
     fn is_useful(&self) -> bool {
         match self {
-            Usefulness::NoWitnesses(set) => !set.is_empty(),
+            Usefulness::NoWitnesses { useful } => *useful,
             Usefulness::WithWitnesses(witnesses) => !witnesses.is_empty(),
         }
     }
@@ -909,33 +555,10 @@ fn extend(&mut self, other: Self) {
             (WithWitnesses(_), WithWitnesses(o)) if o.is_empty() => {}
             (WithWitnesses(s), WithWitnesses(o)) if s.is_empty() => *self = WithWitnesses(o),
             (WithWitnesses(s), WithWitnesses(o)) => s.extend(o),
-            (NoWitnesses(s), NoWitnesses(o)) => s.union(o),
-            _ => unreachable!(),
-        }
-    }
-
-    /// When trying several branches and each returns a `Usefulness`, we need to combine the
-    /// results together.
-    fn merge(pref: ArmType, usefulnesses: impl Iterator<Item = Self>) -> Self {
-        let mut ret = Self::new_not_useful(pref);
-        for u in usefulnesses {
-            ret.extend(u);
-            if let NoWitnesses(subpats) = &ret {
-                if subpats.is_full() {
-                    // Once we reach the full set, more unions won't change the result.
-                    return ret;
-                }
+            (NoWitnesses { useful: s_useful }, NoWitnesses { useful: o_useful }) => {
+                *s_useful = *s_useful || o_useful
             }
-        }
-        ret
-    }
-
-    /// After calculating the usefulness for a branch of an or-pattern, call this to make this
-    /// usefulness mergeable with those from the other branches.
-    fn unsplit_or_pat(self, alt_id: usize, alt_count: usize, pat: &'p Pat<'tcx>) -> Self {
-        match self {
-            NoWitnesses(subpats) => NoWitnesses(subpats.unsplit_or_pat(alt_id, alt_count, pat)),
-            WithWitnesses(_) => bug!(),
+            _ => unreachable!(),
         }
     }
 
@@ -947,10 +570,10 @@ fn apply_constructor(
         pcx: PatCtxt<'_, 'p, 'tcx>,
         matrix: &Matrix<'p, 'tcx>, // used to compute missing ctors
         ctor: &Constructor<'tcx>,
-        ctor_wild_subpatterns: &Fields<'p, 'tcx>,
     ) -> Self {
         match self {
-            WithWitnesses(witnesses) if witnesses.is_empty() => WithWitnesses(witnesses),
+            NoWitnesses { .. } => self,
+            WithWitnesses(ref witnesses) if witnesses.is_empty() => self,
             WithWitnesses(witnesses) => {
                 let new_witnesses = if let Constructor::Missing { .. } = ctor {
                     // We got the special `Missing` constructor, so each of the missing constructors
@@ -958,22 +581,18 @@ fn apply_constructor(
                     let new_patterns = if pcx.is_non_exhaustive {
                         // Here we don't want the user to try to list all variants, we want them to add
                         // a wildcard, so we only suggest that.
-                        vec![
-                            Fields::wildcards(pcx, &Constructor::NonExhaustive)
-                                .apply(pcx, &Constructor::NonExhaustive),
-                        ]
+                        vec![DeconstructedPat::wildcard(pcx.ty)]
                     } else {
                         let mut split_wildcard = SplitWildcard::new(pcx);
-                        split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
+                        split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
                         // Construct for each missing constructor a "wild" version of this
                         // constructor, that matches everything that can be built with
                         // it. For example, if `ctor` is a `Constructor::Variant` for
                         // `Option::Some`, we get the pattern `Some(_)`.
                         split_wildcard
                             .iter_missing(pcx)
-                            .map(|missing_ctor| {
-                                Fields::wildcards(pcx, missing_ctor).apply(pcx, missing_ctor)
-                            })
+                            .cloned()
+                            .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
                             .collect()
                     };
 
@@ -981,21 +600,25 @@ fn apply_constructor(
                         .into_iter()
                         .flat_map(|witness| {
                             new_patterns.iter().map(move |pat| {
-                                let mut witness = witness.clone();
-                                witness.0.push(pat.clone());
-                                witness
+                                Witness(
+                                    witness
+                                        .0
+                                        .iter()
+                                        .chain(once(pat))
+                                        .map(DeconstructedPat::clone_and_forget_reachability)
+                                        .collect(),
+                                )
                             })
                         })
                         .collect()
                 } else {
                     witnesses
                         .into_iter()
-                        .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns))
+                        .map(|witness| witness.apply_constructor(pcx, &ctor))
                         .collect()
                 };
                 WithWitnesses(new_witnesses)
             }
-            NoWitnesses(subpats) => NoWitnesses(subpats.unspecialize(ctor_wild_subpatterns.len())),
         }
     }
 }
@@ -1039,12 +662,12 @@ enum ArmType {
 ///     `Witness(vec![Pair(Some(_), true)])`
 ///
 /// The final `Pair(Some(_), true)` is then the resulting witness.
-#[derive(Clone, Debug)]
-crate struct Witness<'tcx>(Vec<Pat<'tcx>>);
+#[derive(Debug)]
+crate struct Witness<'p, 'tcx>(Vec<DeconstructedPat<'p, 'tcx>>);
 
-impl<'tcx> Witness<'tcx> {
+impl<'p, 'tcx> Witness<'p, 'tcx> {
     /// Asserts that the witness contains a single pattern, and returns it.
-    fn single_pattern(self) -> Pat<'tcx> {
+    fn single_pattern(self) -> DeconstructedPat<'p, 'tcx> {
         assert_eq!(self.0.len(), 1);
         self.0.into_iter().next().unwrap()
     }
@@ -1062,17 +685,13 @@ fn single_pattern(self) -> Pat<'tcx> {
     ///
     /// left_ty: struct X { a: (bool, &'static str), b: usize}
     /// pats: [(false, "foo"), 42]  => X { a: (false, "foo"), b: 42 }
-    fn apply_constructor<'p>(
-        mut self,
-        pcx: PatCtxt<'_, 'p, 'tcx>,
-        ctor: &Constructor<'tcx>,
-        ctor_wild_subpatterns: &Fields<'p, 'tcx>,
-    ) -> Self {
+    fn apply_constructor(mut self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Self {
         let pat = {
             let len = self.0.len();
-            let arity = ctor_wild_subpatterns.len();
+            let arity = ctor.arity(pcx);
             let pats = self.0.drain((len - arity)..).rev();
-            ctor_wild_subpatterns.replace_fields(pcx.cx, pats).apply(pcx, ctor)
+            let fields = Fields::from_iter(pcx.cx, pats);
+            DeconstructedPat::new(ctor.clone(), fields, pcx.ty, DUMMY_SP)
         };
 
         self.0.push(pat);
@@ -1090,9 +709,9 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>(
     scrut_ty: Ty<'tcx>,
     sp: Span,
     hir_id: HirId,
-    witnesses: Vec<Pat<'tcx>>,
+    witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
 ) {
-    let joined_patterns = joined_uncovered_patterns(&witnesses);
+    let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
     cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, |build| {
         let mut lint = build.build("some variants are not matched explicitly");
         lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
@@ -1163,56 +782,52 @@ fn is_useful<'p, 'tcx>(
     assert!(rows.iter().all(|r| r.len() == v.len()));
 
     // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
-    let ty = matrix.heads().next().map_or(v.head().ty, |r| r.ty);
+    let ty = matrix.heads().next().map_or(v.head().ty(), |r| r.ty());
     let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
-    let pcx = PatCtxt { cx, ty, span: v.head().span, is_top_level, is_non_exhaustive };
+    let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive };
 
     // If the first pattern is an or-pattern, expand it.
-    let ret = if is_or_pat(v.head()) {
+    let mut ret = Usefulness::new_not_useful(witness_preference);
+    if v.head().is_or_pat() {
         debug!("expanding or-pattern");
-        let v_head = v.head();
-        let vs: Vec<_> = v.expand_or_pat().collect();
-        let alt_count = vs.len();
         // We try each or-pattern branch in turn.
         let mut matrix = matrix.clone();
-        let usefulnesses = vs.into_iter().enumerate().map(|(i, v)| {
+        for v in v.expand_or_pat() {
             let usefulness =
                 is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
+            ret.extend(usefulness);
             // If pattern has a guard don't add it to the matrix.
             if !is_under_guard {
                 // We push the already-seen patterns into the matrix in order to detect redundant
                 // branches like `Some(_) | Some(0)`.
                 matrix.push(v);
             }
-            usefulness.unsplit_or_pat(i, alt_count, v_head)
-        });
-        Usefulness::merge(witness_preference, usefulnesses)
+        }
     } else {
-        let v_ctor = v.head_ctor(cx);
+        let v_ctor = v.head().ctor();
         if let Constructor::IntRange(ctor_range) = &v_ctor {
             // Lint on likely incorrect range patterns (#63987)
             ctor_range.lint_overlapping_range_endpoints(
                 pcx,
-                matrix.head_ctors_and_spans(cx),
+                matrix.heads(),
                 matrix.column_count().unwrap_or(0),
                 hir_id,
             )
         }
         // We split the head constructor of `v`.
-        let split_ctors = v_ctor.split(pcx, matrix.head_ctors(cx));
+        let split_ctors = v_ctor.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
         let is_non_exhaustive_and_wild = is_non_exhaustive && v_ctor.is_wildcard();
         // For each constructor, we compute whether there's a value that starts with it that would
         // witness the usefulness of `v`.
         let start_matrix = &matrix;
-        let usefulnesses = split_ctors.into_iter().map(|ctor| {
+        for ctor in split_ctors {
             debug!("specialize({:?})", ctor);
             // We cache the result of `Fields::wildcards` because it is used a lot.
-            let ctor_wild_subpatterns = Fields::wildcards(pcx, &ctor);
-            let spec_matrix =
-                start_matrix.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns);
-            let v = v.pop_head_constructor(&ctor_wild_subpatterns);
+            let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor);
+            let v = v.pop_head_constructor(cx, &ctor);
             let usefulness =
                 is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false);
+            let usefulness = usefulness.apply_constructor(pcx, start_matrix, &ctor);
 
             // When all the conditions are met we have a match with a `non_exhaustive` enum
             // that has the potential to trigger the `non_exhaustive_omitted_patterns` lint.
@@ -1229,29 +844,31 @@ fn is_useful<'p, 'tcx>(
             {
                 let patterns = {
                     let mut split_wildcard = SplitWildcard::new(pcx);
-                    split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
+                    split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
                     // Construct for each missing constructor a "wild" version of this
                     // constructor, that matches everything that can be built with
                     // it. For example, if `ctor` is a `Constructor::Variant` for
                     // `Option::Some`, we get the pattern `Some(_)`.
                     split_wildcard
                         .iter_missing(pcx)
-                        // Filter out the `Constructor::NonExhaustive` variant it's meaningless
-                        // to our lint
+                        // Filter out the `NonExhaustive` because we want to list only real
+                        // variants.
                         .filter(|c| !c.is_non_exhaustive())
-                        .map(|missing_ctor| {
-                            Fields::wildcards(pcx, missing_ctor).apply(pcx, missing_ctor)
-                        })
+                        .cloned()
+                        .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
                         .collect::<Vec<_>>()
                 };
 
                 lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns);
             }
 
-            usefulness.apply_constructor(pcx, start_matrix, &ctor, &ctor_wild_subpatterns)
-        });
-        Usefulness::merge(witness_preference, usefulnesses)
-    };
+            ret.extend(usefulness);
+        }
+    }
+
+    if ret.is_useful() {
+        v.head().set_reachable();
+    }
 
     debug!(?ret);
     ret
@@ -1261,7 +878,7 @@ fn is_useful<'p, 'tcx>(
 #[derive(Clone, Copy)]
 crate struct MatchArm<'p, 'tcx> {
     /// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`.
-    crate pat: &'p Pat<'tcx>,
+    crate pat: &'p DeconstructedPat<'p, 'tcx>,
     crate hir_id: HirId,
     crate has_guard: bool,
 }
@@ -1283,7 +900,7 @@ fn is_useful<'p, 'tcx>(
     crate arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Reachability)>,
     /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
     /// exhaustiveness.
-    crate non_exhaustiveness_witnesses: Vec<Pat<'tcx>>,
+    crate non_exhaustiveness_witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
 }
 
 /// The entrypoint for the usefulness algorithm. Computes whether a match is exhaustive and which
@@ -1303,27 +920,25 @@ fn is_useful<'p, 'tcx>(
         .copied()
         .map(|arm| {
             let v = PatStack::from_pattern(arm.pat);
-            let usefulness = is_useful(cx, &matrix, &v, RealArm, arm.hir_id, arm.has_guard, true);
+            is_useful(cx, &matrix, &v, RealArm, arm.hir_id, arm.has_guard, true);
             if !arm.has_guard {
                 matrix.push(v);
             }
-            let reachability = match usefulness {
-                NoWitnesses(subpats) if subpats.is_empty() => Reachability::Unreachable,
-                NoWitnesses(subpats) => {
-                    Reachability::Reachable(subpats.list_unreachable_spans().unwrap())
-                }
-                WithWitnesses(..) => bug!(),
+            let reachability = if arm.pat.is_reachable() {
+                Reachability::Reachable(arm.pat.unreachable_spans())
+            } else {
+                Reachability::Unreachable
             };
             (arm, reachability)
         })
         .collect();
 
-    let wild_pattern = cx.pattern_arena.alloc(Pat::wildcard_from_ty(scrut_ty));
+    let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty));
     let v = PatStack::from_pattern(wild_pattern);
     let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true);
     let non_exhaustiveness_witnesses = match usefulness {
         WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(),
-        NoWitnesses(_) => bug!(),
+        NoWitnesses { .. } => bug!(),
     };
     UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
 }
index 771ad90af28d67e08b306a755f4723129f46bca7..474f4f2a79b2a7475a25344d49f0f9193b48d04c 100644 (file)
@@ -602,6 +602,7 @@ fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domai
 impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
     type Idx = InitIndex;
 
+    #[instrument(skip(self, trans), level = "debug")]
     fn statement_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -613,24 +614,19 @@ fn statement_effect(
         let init_loc_map = &move_data.init_loc_map;
         let rev_lookup = &move_data.rev_lookup;
 
-        debug!(
-            "statement {:?} at loc {:?} initializes move_indexes {:?}",
-            stmt, location, &init_loc_map[location]
-        );
+        debug!("initializes move_indexes {:?}", &init_loc_map[location]);
         trans.gen_all(init_loc_map[location].iter().copied());
 
         if let mir::StatementKind::StorageDead(local) = stmt.kind {
             // End inits for StorageDead, so that an immutable variable can
             // be reinitialized on the next iteration of the loop.
             let move_path_index = rev_lookup.find_local(local);
-            debug!(
-                "stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
-                stmt, location, &init_path_map[move_path_index]
-            );
+            debug!("clears the ever initialized status of {:?}", init_path_map[move_path_index]);
             trans.kill_all(init_path_map[move_path_index].iter().copied());
         }
     }
 
+    #[instrument(skip(self, trans, _terminator), level = "debug")]
     fn terminator_effect(
         &self,
         trans: &mut impl GenKill<Self::Idx>,
@@ -640,10 +636,8 @@ fn terminator_effect(
         let (body, move_data) = (self.body, self.move_data());
         let term = body[location.block].terminator();
         let init_loc_map = &move_data.init_loc_map;
-        debug!(
-            "terminator {:?} at loc {:?} initializes move_indexes {:?}",
-            term, location, &init_loc_map[location]
-        );
+        debug!(?term);
+        debug!("initializes move_indexes {:?}", init_loc_map[location]);
         trans.gen_all(
             init_loc_map[location]
                 .iter()
index d6ff5a7e90b2173a2d2081a22db86aa7d11c202c..ab1f47c81db975129f9827786f8d0da7e9c05c62 100644 (file)
@@ -198,7 +198,7 @@ impl<'a> Resolver<'a> {
                 err.span_label(first_use_span, format!("first use of `{}`", name));
                 err
             }
-            ResolutionError::MethodNotMemberOfTrait(method, trait_) => {
+            ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => {
                 let mut err = struct_span_err!(
                     self.session,
                     span,
@@ -208,9 +208,17 @@ impl<'a> Resolver<'a> {
                     trait_
                 );
                 err.span_label(span, format!("not a member of trait `{}`", trait_));
+                if let Some(candidate) = candidate {
+                    err.span_suggestion(
+                        method.span,
+                        "there is an associated function with a similar name",
+                        candidate.to_ident_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
                 err
             }
-            ResolutionError::TypeNotMemberOfTrait(type_, trait_) => {
+            ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => {
                 let mut err = struct_span_err!(
                     self.session,
                     span,
@@ -220,9 +228,17 @@ impl<'a> Resolver<'a> {
                     trait_
                 );
                 err.span_label(span, format!("not a member of trait `{}`", trait_));
+                if let Some(candidate) = candidate {
+                    err.span_suggestion(
+                        type_.span,
+                        "there is an associated type with a similar name",
+                        candidate.to_ident_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
                 err
             }
-            ResolutionError::ConstNotMemberOfTrait(const_, trait_) => {
+            ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => {
                 let mut err = struct_span_err!(
                     self.session,
                     span,
@@ -232,6 +248,14 @@ impl<'a> Resolver<'a> {
                     trait_
                 );
                 err.span_label(span, format!("not a member of trait `{}`", trait_));
+                if let Some(candidate) = candidate {
+                    err.span_suggestion(
+                        const_.span,
+                        "there is an associated constant with a similar name",
+                        candidate.to_ident_string(),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
                 err
             }
             ResolutionError::VariableNotBoundInPattern(binding_error) => {
@@ -949,7 +973,15 @@ fn lookup_import_candidates_from_module<FilterFn>(
 
         let import_suggestions =
             self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);
-        show_candidates(err, None, &import_suggestions, false, true);
+        show_candidates(
+            &self.definitions,
+            self.session,
+            err,
+            None,
+            &import_suggestions,
+            false,
+            true,
+        );
 
         if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
             let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident);
@@ -1689,6 +1721,8 @@ fn find_span_immediately_after_crate_name(
 /// entities with that name in all crates. This method allows outputting the
 /// results of this search in a programmer-friendly way
 crate fn show_candidates(
+    definitions: &rustc_hir::definitions::Definitions,
+    session: &Session,
     err: &mut DiagnosticBuilder<'_>,
     // This is `None` if all placement locations are inside expansions
     use_placement_span: Option<Span>,
@@ -1700,43 +1734,111 @@ fn find_span_immediately_after_crate_name(
         return;
     }
 
+    let mut accessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new();
+    let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new();
+
+    candidates.iter().for_each(|c| {
+        (if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings })
+            .push((path_names_to_string(&c.path), c.descr, c.did))
+    });
+
     // we want consistent results across executions, but candidates are produced
     // by iterating through a hash map, so make sure they are ordered:
-    let mut path_strings: Vec<_> =
-        candidates.iter().map(|c| path_names_to_string(&c.path)).collect();
+    for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] {
+        path_strings.sort_by(|a, b| a.0.cmp(&b.0));
+        let core_path_strings =
+            path_strings.drain_filter(|p| p.0.starts_with("core::")).collect::<Vec<_>>();
+        path_strings.extend(core_path_strings);
+        path_strings.dedup_by(|a, b| a.0 == b.0);
+    }
 
-    path_strings.sort();
-    let core_path_strings =
-        path_strings.drain_filter(|p| p.starts_with("core::")).collect::<Vec<String>>();
-    path_strings.extend(core_path_strings);
-    path_strings.dedup();
+    if !accessible_path_strings.is_empty() {
+        let (determiner, kind) = if accessible_path_strings.len() == 1 {
+            ("this", accessible_path_strings[0].1)
+        } else {
+            ("one of these", "items")
+        };
 
-    let (determiner, kind) = if candidates.len() == 1 {
-        ("this", candidates[0].descr)
-    } else {
-        ("one of these", "items")
-    };
-
-    let instead = if instead { " instead" } else { "" };
-    let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
-
-    if let Some(span) = use_placement_span {
-        for candidate in &mut path_strings {
-            // produce an additional newline to separate the new use statement
-            // from the directly following item.
-            let additional_newline = if found_use { "" } else { "\n" };
-            *candidate = format!("use {};\n{}", candidate, additional_newline);
-        }
+        let instead = if instead { " instead" } else { "" };
+        let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
 
-        err.span_suggestions(span, &msg, path_strings.into_iter(), Applicability::Unspecified);
-    } else {
-        msg.push(':');
+        if let Some(span) = use_placement_span {
+            for candidate in &mut accessible_path_strings {
+                // produce an additional newline to separate the new use statement
+                // from the directly following item.
+                let additional_newline = if found_use { "" } else { "\n" };
+                candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
+            }
+
+            err.span_suggestions(
+                span,
+                &msg,
+                accessible_path_strings.into_iter().map(|a| a.0),
+                Applicability::Unspecified,
+            );
+        } else {
+            msg.push(':');
+
+            for candidate in accessible_path_strings {
+                msg.push('\n');
+                msg.push_str(&candidate.0);
+            }
 
-        for candidate in path_strings {
-            msg.push('\n');
-            msg.push_str(&candidate);
+            err.note(&msg);
         }
+    } else {
+        assert!(!inaccessible_path_strings.is_empty());
+
+        if inaccessible_path_strings.len() == 1 {
+            let (name, descr, def_id) = &inaccessible_path_strings[0];
+            let msg = format!("{} `{}` exists but is inaccessible", descr, name);
+
+            if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
+                let span = definitions.def_span(local_def_id);
+                let span = session.source_map().guess_head_span(span);
+                let mut multi_span = MultiSpan::from_span(span);
+                multi_span.push_span_label(span, "not accessible".to_string());
+                err.span_note(multi_span, &msg);
+            } else {
+                err.note(&msg);
+            }
+        } else {
+            let (_, descr_first, _) = &inaccessible_path_strings[0];
+            let descr = if inaccessible_path_strings
+                .iter()
+                .skip(1)
+                .all(|(_, descr, _)| descr == descr_first)
+            {
+                format!("{}", descr_first)
+            } else {
+                "item".to_string()
+            };
+
+            let mut msg = format!("these {}s exist but are inaccessible", descr);
+            let mut has_colon = false;
 
-        err.note(&msg);
+            let mut spans = Vec::new();
+            for (name, _, def_id) in &inaccessible_path_strings {
+                if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
+                    let span = definitions.def_span(local_def_id);
+                    let span = session.source_map().guess_head_span(span);
+                    spans.push((name, span));
+                } else {
+                    if !has_colon {
+                        msg.push(':');
+                        has_colon = true;
+                    }
+                    msg.push('\n');
+                    msg.push_str(name);
+                }
+            }
+
+            let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect());
+            for (name, span) in spans {
+                multi_span.push_span_label(span, format!("`{}`: not accessible", name));
+            }
+
+            err.span_note(multi_span, &msg);
+        }
     }
 }
index 3c48a76224fd964f8d750978ee035f80223d4452..9563325796538dab148fc9d45d5f3448ba6a0980 100644 (file)
@@ -1309,14 +1309,15 @@ fn resolve_implementation(
                                     use crate::ResolutionError::*;
                                     match &item.kind {
                                         AssocItemKind::Const(_default, _ty, _expr) => {
-                                            debug!("resolve_implementation AssocItemKind::Const",);
+                                            debug!("resolve_implementation AssocItemKind::Const");
                                             // If this is a trait impl, ensure the const
                                             // exists in trait
                                             this.check_trait_item(
                                                 item.ident,
+                                                &item.kind,
                                                 ValueNS,
                                                 item.span,
-                                                |n, s| ConstNotMemberOfTrait(n, s),
+                                                |i, s, c| ConstNotMemberOfTrait(i, s, c),
                                             );
 
                                             // We allow arbitrary const expressions inside of associated consts,
@@ -1338,6 +1339,7 @@ fn resolve_implementation(
                                             );
                                         }
                                         AssocItemKind::Fn(box FnKind(.., generics, _)) => {
+                                            debug!("resolve_implementation AssocItemKind::Fn");
                                             // We also need a new scope for the impl item type parameters.
                                             this.with_generic_param_rib(
                                                 generics,
@@ -1347,9 +1349,10 @@ fn resolve_implementation(
                                                     // exists in trait
                                                     this.check_trait_item(
                                                         item.ident,
+                                                        &item.kind,
                                                         ValueNS,
                                                         item.span,
-                                                        |n, s| MethodNotMemberOfTrait(n, s),
+                                                        |i, s, c| MethodNotMemberOfTrait(i, s, c),
                                                     );
 
                                                     visit::walk_assoc_item(
@@ -1366,6 +1369,7 @@ fn resolve_implementation(
                                             _,
                                             _,
                                         )) => {
+                                            debug!("resolve_implementation AssocItemKind::TyAlias");
                                             // We also need a new scope for the impl item type parameters.
                                             this.with_generic_param_rib(
                                                 generics,
@@ -1375,9 +1379,10 @@ fn resolve_implementation(
                                                     // exists in trait
                                                     this.check_trait_item(
                                                         item.ident,
+                                                        &item.kind,
                                                         TypeNS,
                                                         item.span,
-                                                        |n, s| TypeNotMemberOfTrait(n, s),
+                                                        |i, s, c| TypeNotMemberOfTrait(i, s, c),
                                                     );
 
                                                     visit::walk_assoc_item(
@@ -1401,9 +1406,15 @@ fn resolve_implementation(
         });
     }
 
-    fn check_trait_item<F>(&mut self, ident: Ident, ns: Namespace, span: Span, err: F)
-    where
-        F: FnOnce(Symbol, &str) -> ResolutionError<'_>,
+    fn check_trait_item<F>(
+        &mut self,
+        ident: Ident,
+        kind: &AssocItemKind,
+        ns: Namespace,
+        span: Span,
+        err: F,
+    ) where
+        F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
     {
         // If there is a TraitRef in scope for an impl, then the method must be in the
         // trait.
@@ -1420,8 +1431,9 @@ fn check_trait_item<F>(&mut self, ident: Ident, ns: Namespace, span: Span, err:
                 )
                 .is_err()
             {
+                let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
                 let path = &self.current_trait_ref.as_ref().unwrap().1.path;
-                self.report_error(span, err(ident.name, &path_names_to_string(path)));
+                self.report_error(span, err(ident, &path_names_to_string(path), candidate));
             }
         }
     }
index 84219873d55b4e9eb5b3f4139bb03aa17079e915..e57e7db328549fd7954d421762c9487647af4568 100644 (file)
@@ -7,8 +7,8 @@
 
 use rustc_ast::visit::FnKind;
 use rustc_ast::{
-    self as ast, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, NodeId, Path, Ty,
-    TyKind,
+    self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
+    NodeId, Path, Ty, TyKind,
 };
 use rustc_ast_pretty::pprust::path_segment_to_string;
 use rustc_data_structures::fx::FxHashSet;
@@ -1150,6 +1150,40 @@ fn smart_resolve_context_dependent_help(
         true
     }
 
+    /// Given the target `ident` and `kind`, search for the similarly named associated item
+    /// in `self.current_trait_ref`.
+    crate fn find_similarly_named_assoc_item(
+        &mut self,
+        ident: Symbol,
+        kind: &AssocItemKind,
+    ) -> Option<Symbol> {
+        let module = if let Some((module, _)) = self.current_trait_ref {
+            module
+        } else {
+            return None;
+        };
+        if ident == kw::Underscore {
+            // We do nothing for `_`.
+            return None;
+        }
+
+        let resolutions = self.r.resolutions(module);
+        let targets = resolutions
+            .borrow()
+            .iter()
+            .filter_map(|(key, res)| res.borrow().binding.map(|binding| (key, binding.res())))
+            .filter(|(_, res)| match (kind, res) {
+                (AssocItemKind::Const(..), Res::Def(DefKind::AssocConst, _)) => true,
+                (AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true,
+                (AssocItemKind::TyAlias(..), Res::Def(DefKind::AssocTy, _)) => true,
+                _ => false,
+            })
+            .map(|(key, _)| key.ident.name)
+            .collect::<Vec<_>>();
+
+        find_best_match_for_name(&targets, ident, None)
+    }
+
     fn lookup_assoc_candidate<FilterFn>(
         &mut self,
         ident: Ident,
index 84e7c68713f231d9b231489943ba24d4fa7b2d3d..eb6f302a11da825aa195388a3bc29df9333c4f20 100644 (file)
@@ -2740,6 +2740,7 @@ fn visit_fn_like_elision(
         for input in inputs {
             gather.visit_ty(input);
         }
+        trace!(?gather.anon_count);
         let late_bound_vars = self.map.late_bound_vars.entry(hir_id).or_default();
         let named_late_bound_vars = late_bound_vars.len() as u32;
         late_bound_vars.extend(
@@ -3028,6 +3029,7 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
                 NestedVisitorMap::None
             }
 
+            #[instrument(skip(self), level = "trace")]
             fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
                 // If we enter a `BareFn`, then we enter a *new* binding scope
                 if let hir::TyKind::BareFn(_) = ty.kind {
@@ -3048,6 +3050,7 @@ fn visit_generic_args(
                 intravisit::walk_generic_args(self, path_span, generic_args)
             }
 
+            #[instrument(skip(self), level = "trace")]
             fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
                 if lifetime_ref.is_elided() {
                     self.anon_count += 1;
index 1cbe8f41d92b0704714e881bc02613765f9dbffd..ff7f465dbc6d7769cde1e56e29c4686085810878 100644 (file)
@@ -20,6 +20,9 @@
 #![recursion_limit = "256"]
 #![allow(rustdoc::private_intra_doc_links)]
 
+#[macro_use]
+extern crate tracing;
+
 pub use rustc_hir::def::{Namespace, PerNS};
 
 use Determinacy::*;
@@ -203,11 +206,11 @@ enum ResolutionError<'a> {
     /// parameter list.
     NameAlreadyUsedInParameterList(Symbol, Span),
     /// Error E0407: method is not a member of trait.
-    MethodNotMemberOfTrait(Symbol, &'a str),
+    MethodNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
     /// Error E0437: type is not a member of trait.
-    TypeNotMemberOfTrait(Symbol, &'a str),
+    TypeNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
     /// Error E0438: const is not a member of trait.
-    ConstNotMemberOfTrait(Symbol, &'a str),
+    ConstNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
     /// Error E0408: variable `{}` is not bound in all patterns.
     VariableNotBoundInPattern(&'a BindingError),
     /// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm.
@@ -2966,7 +2969,15 @@ fn report_with_use_injections(&mut self, krate: &Crate) {
                 (None, false)
             };
             if !candidates.is_empty() {
-                diagnostics::show_candidates(&mut err, span, &candidates, instead, found_use);
+                diagnostics::show_candidates(
+                    &self.definitions,
+                    self.session,
+                    &mut err,
+                    span,
+                    &candidates,
+                    instead,
+                    found_use,
+                );
             } else if let Some((span, msg, sugg, appl)) = suggestion {
                 err.span_suggestion(span, msg, sugg, appl);
             }
index 27215556045be2fe15eddda96a73f816f532262b..87f9190114109c067d1ce6c0306d9a177fe88be9 100644 (file)
@@ -2,10 +2,9 @@
 use crate::code_stats::CodeStats;
 pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
 use crate::config::{self, CrateType, OutputType, SwitchWithOptPath};
-use crate::filesearch;
-use crate::lint::{self, LintId};
 use crate::parse::ParseSess;
 use crate::search_paths::{PathKind, SearchPath};
+use crate::{filesearch, lint};
 
 pub use rustc_ast::attr::MarkedAttrs;
 pub use rustc_ast::Attribute;
 use std::sync::Arc;
 use std::time::Duration;
 
-pub trait SessionLintStore: sync::Send + sync::Sync {
-    fn name_to_lint(&self, lint_name: &str) -> LintId;
-}
-
 pub struct OptimizationFuel {
     /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`.
     remaining: u64,
@@ -153,8 +148,6 @@ pub struct Session {
 
     features: OnceCell<rustc_feature::Features>,
 
-    lint_store: OnceCell<Lrc<dyn SessionLintStore>>,
-
     incr_comp_session: OneThread<RefCell<IncrCompSession>>,
     /// Used for incremental compilation tests. Will only be populated if
     /// `-Zquery-dep-graph` is specified.
@@ -591,13 +584,6 @@ pub fn init_features(&self, features: rustc_feature::Features) {
         }
     }
 
-    pub fn init_lint_store(&self, lint_store: Lrc<dyn SessionLintStore>) {
-        self.lint_store
-            .set(lint_store)
-            .map_err(|_| ())
-            .expect("`lint_store` was initialized twice");
-    }
-
     /// Calculates the flavor of LTO to use for this compilation.
     pub fn lto(&self) -> config::Lto {
         // If our target has codegen requirements ignore the command line
@@ -1315,7 +1301,6 @@ pub fn build_session(
         crate_types: OnceCell::new(),
         stable_crate_id: OnceCell::new(),
         features: OnceCell::new(),
-        lint_store: OnceCell::new(),
         incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
         cgu_reuse_tracker,
         prof,
diff --git a/compiler/rustc_target/src/spec/aarch64_kmc_solid_asp3.rs b/compiler/rustc_target/src/spec/aarch64_kmc_solid_asp3.rs
new file mode 100644 (file)
index 0000000..61e3be6
--- /dev/null
@@ -0,0 +1,19 @@
+use super::{RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+    let base = super::solid_base::opts("asp3");
+    Target {
+        llvm_target: "aarch64-unknown-none".to_string(),
+        pointer_width: 64,
+        data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(),
+        arch: "aarch64".to_string(),
+        options: TargetOptions {
+            linker: Some("aarch64-kmc-elf-gcc".to_owned()),
+            features: "+neon,+fp-armv8".to_string(),
+            relocation_model: RelocModel::Static,
+            disable_redzone: true,
+            max_atomic_width: Some(128),
+            ..base
+        },
+    }
+}
diff --git a/compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabi.rs b/compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabi.rs
new file mode 100644 (file)
index 0000000..344c480
--- /dev/null
@@ -0,0 +1,19 @@
+use super::{RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+    let base = super::solid_base::opts("asp3");
+    Target {
+        llvm_target: "armv7a-none-eabi".to_string(),
+        pointer_width: 32,
+        data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
+        arch: "arm".to_string(),
+        options: TargetOptions {
+            linker: Some("arm-kmc-eabi-gcc".to_owned()),
+            features: "+v7,+soft-float,+thumb2,-neon".to_string(),
+            relocation_model: RelocModel::Static,
+            disable_redzone: true,
+            max_atomic_width: Some(64),
+            ..base
+        },
+    }
+}
diff --git a/compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_kmc_solid_asp3_eabihf.rs
new file mode 100644 (file)
index 0000000..3755024
--- /dev/null
@@ -0,0 +1,19 @@
+use super::{RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+    let base = super::solid_base::opts("asp3");
+    Target {
+        llvm_target: "armv7a-none-eabihf".to_string(),
+        pointer_width: 32,
+        data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
+        arch: "arm".to_string(),
+        options: TargetOptions {
+            linker: Some("arm-kmc-eabi-gcc".to_owned()),
+            features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(),
+            relocation_model: RelocModel::Static,
+            disable_redzone: true,
+            max_atomic_width: Some(64),
+            ..base
+        },
+    }
+}
index c947721d63d39bf5f0d9c98fcb0c1dbbfc75ac44..d18e5823cb80c1d406bdf9ec3bfe7d5c5451e1a9 100644 (file)
@@ -75,6 +75,7 @@
 mod openbsd_base;
 mod redox_base;
 mod solaris_base;
+mod solid_base;
 mod thumb_base;
 mod uefi_msvc_base;
 mod vxworks_base;
@@ -932,6 +933,10 @@ fn $module() {
     ("powerpc-wrs-vxworks-spe", powerpc_wrs_vxworks_spe),
     ("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks),
 
+    ("aarch64-kmc-solid_asp3", aarch64_kmc_solid_asp3),
+    ("armv7a-kmc-solid_asp3-eabi", armv7a_kmc_solid_asp3_eabi),
+    ("armv7a-kmc-solid_asp3-eabihf", armv7a_kmc_solid_asp3_eabihf),
+
     ("mipsel-sony-psp", mipsel_sony_psp),
     ("mipsel-unknown-none", mipsel_unknown_none),
     ("thumbv4t-none-eabi", thumbv4t_none_eabi),
diff --git a/compiler/rustc_target/src/spec/solid_base.rs b/compiler/rustc_target/src/spec/solid_base.rs
new file mode 100644 (file)
index 0000000..c6a279d
--- /dev/null
@@ -0,0 +1,12 @@
+use super::FramePointer;
+use crate::spec::TargetOptions;
+
+pub fn opts(kernel: &str) -> TargetOptions {
+    TargetOptions {
+        os: format!("solid_{}", kernel),
+        vendor: "kmc".to_string(),
+        frame_pointer: FramePointer::NonLeaf,
+        has_elf_tls: true,
+        ..Default::default()
+    }
+}
index a89796f172c5a588c398ce440034b07936bd2857..53afe4ca068c4cf24f0601ee3c38ddb7a8cbc855 100644 (file)
@@ -5,7 +5,7 @@
 use rustc_infer::infer::InferCtxt;
 use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness};
 use rustc_middle::ty::{ToPredicate, TypeFoldable};
-use rustc_session::DiagnosticMessageId;
+use rustc_session::{DiagnosticMessageId, Limit};
 use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::Span;
 
@@ -217,7 +217,10 @@ pub fn silence_errors(mut self) -> Self {
 
 pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
     // We've reached the recursion limit, error gracefully.
-    let suggested_limit = tcx.recursion_limit() * 2;
+    let suggested_limit = match tcx.recursion_limit() {
+        Limit(0) => Limit(2),
+        limit => limit * 2,
+    };
     let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty);
     let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg);
     let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
@@ -231,7 +234,8 @@ pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Spa
         )
         .span_label(span, "deref recursion limit reached")
         .help(&format!(
-            "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
+            "consider increasing the recursion limit by adding a \
+             `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
             suggested_limit,
             tcx.crate_name(LOCAL_CRATE),
         ))
index 028f6e89f18f127eac726cb7177d1ca8d72373ee..c2205462680fc88d48aa100d1c3f172752392e06 100644 (file)
@@ -517,6 +517,7 @@ fn infer_opaque_definition_from_instantiation(
         debug!(?id_substs);
         let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
             substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect();
+        debug!("map = {:#?}", map);
 
         // Convert the type from the function into a type valid outside
         // the function, by replacing invalid regions with 'static,
@@ -672,6 +673,7 @@ fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match r {
             // Ignore bound regions and `'static` regions that appear in the
@@ -1098,18 +1100,17 @@ fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: LocalDefId, opaque_hir_id: hi
 ///
 /// Requires that trait definitions have been processed so that we can
 /// elaborate predicates and walk supertraits.
+#[instrument(skip(tcx, predicates), level = "debug")]
 crate fn required_region_bounds(
     tcx: TyCtxt<'tcx>,
     erased_self_ty: Ty<'tcx>,
     predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
 ) -> Vec<ty::Region<'tcx>> {
-    debug!("required_region_bounds(erased_self_ty={:?})", erased_self_ty);
-
     assert!(!erased_self_ty.has_escaping_bound_vars());
 
     traits::elaborate_predicates(tcx, predicates)
         .filter_map(|obligation| {
-            debug!("required_region_bounds(obligation={:?})", obligation);
+            debug!(?obligation);
             match obligation.predicate.kind().skip_binder() {
                 ty::PredicateKind::Projection(..)
                 | ty::PredicateKind::Trait(..)
index e435154d9318e7b087a7bc98c84097b4f3e51b64..b751918463b31ef73a192880d31abc3e6bbe372d 100644 (file)
@@ -1195,13 +1195,13 @@ fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -
         false
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn report_fulfillment_error(
         &self,
         error: &FulfillmentError<'tcx>,
         body_id: Option<hir::BodyId>,
         fallback_has_occurred: bool,
     ) {
-        debug!("report_fulfillment_error({:?})", error);
         match error.code {
             FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
                 self.report_selection_error(
@@ -1528,6 +1528,7 @@ fn mk_trait_obligation_with_new_self_ty(
         )
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn maybe_report_ambiguity(
         &self,
         obligation: &PredicateObligation<'tcx>,
@@ -1542,8 +1543,7 @@ fn maybe_report_ambiguity(
         let span = obligation.cause.span;
 
         debug!(
-            "maybe_report_ambiguity(predicate={:?}, obligation={:?} body_id={:?}, code={:?})",
-            predicate, obligation, body_id, obligation.cause.code,
+            ?predicate, ?obligation.cause.code,
         );
 
         // Ambiguity errors are often caused as fallout from earlier
@@ -1556,7 +1556,7 @@ fn maybe_report_ambiguity(
         let mut err = match bound_predicate.skip_binder() {
             ty::PredicateKind::Trait(data) => {
                 let trait_ref = bound_predicate.rebind(data.trait_ref);
-                debug!("trait_ref {:?}", trait_ref);
+                debug!(?trait_ref);
 
                 if predicate.references_error() {
                     return;
index 2a51e01471398861252d3b3d96fdf31f88d40154..325126483b9cec23b441d95db63d9c77d7f9cff9 100644 (file)
@@ -22,6 +22,7 @@
     Infer, InferTy, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
 };
 use rustc_middle::ty::{TypeAndMut, TypeckResults};
+use rustc_session::Limit;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, DesugaringKind, ExpnKind, ForLoopLoc, MultiSpan, Span, DUMMY_SP};
@@ -2426,10 +2427,13 @@ fn note_obligation_cause_code<T>(
     }
 
     fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) {
-        let current_limit = self.tcx.recursion_limit();
-        let suggested_limit = current_limit * 2;
+        let suggested_limit = match self.tcx.recursion_limit() {
+            Limit(0) => Limit(2),
+            limit => limit * 2,
+        };
         err.help(&format!(
-            "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
+            "consider increasing the recursion limit by adding a \
+             `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
             suggested_limit,
             self.tcx.crate_name(LOCAL_CRATE),
         ));
index 6d64dc8254bb4a426379aa977ba7790e6000a16a..397f1da75ef01d5dfc69328e14ccb87742ad1091 100644 (file)
@@ -220,6 +220,7 @@ fn candidate_from_obligation_no_cache<'o>(
         self.filter_impls(candidates.pop().unwrap().candidate, stack.obligation)
     }
 
+    #[instrument(skip(self, stack), level = "debug")]
     pub(super) fn assemble_candidates<'o>(
         &mut self,
         stack: &TraitObligationStack<'o, 'tcx>,
index 9e1211336a4b01472026ee7a2cb0d53ecdfb3fac..8554723740fb0f33e173961567f31431fd71b64b 100644 (file)
@@ -601,12 +601,11 @@ fn confirm_generator_candidate(
         Ok(ImplSourceGeneratorData { generator_def_id, substs, nested: obligations })
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn confirm_closure_candidate(
         &mut self,
         obligation: &TraitObligation<'tcx>,
     ) -> Result<ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
-        debug!(?obligation, "confirm_closure_candidate");
-
         let kind = self
             .tcx()
             .fn_trait_kind_from_lang_item(obligation.predicate.def_id())
@@ -680,6 +679,7 @@ fn confirm_closure_candidate(
     /// because these output type parameters should not affect the
     /// selection of the impl. Therefore, if there is a mismatch, we
     /// report an error to the user.
+    #[instrument(skip(self), level = "trace")]
     fn confirm_poly_trait_refs(
         &mut self,
         obligation_cause: ObligationCause<'tcx>,
index 50d6f82ae18fa0a6e9654aecdcebdbf1be3da4d3..af0c7c8f9566dfe65b54ee6acccf030c18e3e731 100644 (file)
@@ -451,6 +451,7 @@ fn evaluation_probe(
     /// Evaluates the predicates in `predicates` recursively. Note that
     /// this applies projections in the predicates, and therefore
     /// is run within an inference probe.
+    #[instrument(skip(self, stack), level = "debug")]
     fn evaluate_predicates_recursively<'o, I>(
         &mut self,
         stack: TraitObligationStackList<'o, 'tcx>,
@@ -460,7 +461,6 @@ fn evaluate_predicates_recursively<'o, I>(
         I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
     {
         let mut result = EvaluatedToOk;
-        debug!(?predicates, "evaluate_predicates_recursively");
         for obligation in predicates {
             let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
             if let EvaluatedToErr = eval {
@@ -683,13 +683,12 @@ fn evaluate_predicate_recursively<'o>(
         result
     }
 
+    #[instrument(skip(self, previous_stack), level = "debug")]
     fn evaluate_trait_predicate_recursively<'o>(
         &mut self,
         previous_stack: TraitObligationStackList<'o, 'tcx>,
         mut obligation: TraitObligation<'tcx>,
     ) -> Result<EvaluationResult, OverflowError> {
-        debug!(?obligation, "evaluate_trait_predicate_recursively");
-
         if !self.intercrate
             && obligation.is_global(self.tcx())
             && obligation
@@ -701,7 +700,7 @@ fn evaluate_trait_predicate_recursively<'o>(
             // If a param env has no global bounds, global obligations do not
             // depend on its particular value in order to work, so we can clear
             // out the param env and get better caching.
-            debug!("evaluate_trait_predicate_recursively - in global");
+            debug!("in global");
             obligation.param_env = obligation.param_env.without_caller_bounds();
         }
 
@@ -753,7 +752,7 @@ fn evaluate_trait_predicate_recursively<'o>(
         } else {
             debug!(?result, "PROVISIONAL");
             debug!(
-                "evaluate_trait_predicate_recursively: caching provisionally because {:?} \
+                "caching provisionally because {:?} \
                  is a cycle participant (at depth {}, reached depth {})",
                 fresh_trait_ref, stack.depth, reached_depth,
             );
@@ -2124,13 +2123,12 @@ fn match_where_clause_trait_ref(
 
     /// Returns `Ok` if `poly_trait_ref` being true implies that the
     /// obligation is satisfied.
+    #[instrument(skip(self), level = "debug")]
     fn match_poly_trait_ref(
         &mut self,
         obligation: &TraitObligation<'tcx>,
         poly_trait_ref: ty::PolyTraitRef<'tcx>,
     ) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
-        debug!(?obligation, ?poly_trait_ref, "match_poly_trait_ref");
-
         self.infcx
             .at(&obligation.cause, obligation.param_env)
             .sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref)
@@ -2174,12 +2172,12 @@ fn push_stack<'o>(
         }
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn closure_trait_ref_unnormalized(
         &mut self,
         obligation: &TraitObligation<'tcx>,
         substs: SubstsRef<'tcx>,
     ) -> ty::PolyTraitRef<'tcx> {
-        debug!(?obligation, ?substs, "closure_trait_ref_unnormalized");
         let closure_sig = substs.as_closure().sig();
 
         debug!(?closure_sig);
index fd0544a47bb7c3e6ed06a3bc34b600e9d9c4cd8b..27d76a6935904efbacd3f6e4e198a7984b525a9c 100644 (file)
@@ -441,6 +441,7 @@ pub fn check_generic_arg_count_for_call(
 
     /// Checks that the correct number of generic arguments have been provided.
     /// This is used both for datatypes and function calls.
+    #[instrument(skip(tcx, gen_pos), level = "debug")]
     pub(crate) fn check_generic_arg_count(
         tcx: TyCtxt<'_>,
         span: Span,
@@ -452,11 +453,6 @@ pub(crate) fn check_generic_arg_count(
         has_self: bool,
         infer_args: bool,
     ) -> GenericArgCountResult {
-        debug!(
-            "check_generic_arg_count(span: {:?}, def_id: {:?}, seg: {:?}, gen_params: {:?}, gen_args: {:?})",
-            span, def_id, seg, gen_params, gen_args
-        );
-
         let default_counts = gen_params.own_defaults();
         let param_counts = gen_params.own_counts();
 
@@ -556,9 +552,12 @@ pub(crate) fn check_generic_arg_count(
         let mut check_types_and_consts =
             |expected_min, expected_max, provided, params_offset, args_offset| {
                 debug!(
-                    "check_types_and_consts(expected_min: {:?}, expected_max: {:?}, \
-                        provided: {:?}, params_offset: {:?}, args_offset: {:?}",
-                    expected_min, expected_max, provided, params_offset, args_offset
+                    ?expected_min,
+                    ?expected_max,
+                    ?provided,
+                    ?params_offset,
+                    ?args_offset,
+                    "check_types_and_consts"
                 );
                 if (expected_min..=expected_max).contains(&provided) {
                     return true;
@@ -589,7 +588,7 @@ pub(crate) fn check_generic_arg_count(
                     }
                 };
 
-                debug!("gen_args_info: {:?}", gen_args_info);
+                debug!(?gen_args_info);
 
                 WrongNumberOfGenericArgs::new(
                     tcx,
@@ -614,8 +613,8 @@ pub(crate) fn check_generic_arg_count(
                     - default_counts.types
                     - default_counts.consts
             };
-            debug!("expected_min: {:?}", expected_min);
-            debug!("arg_counts.lifetimes: {:?}", gen_args.num_lifetime_params());
+            debug!(?expected_min);
+            debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params());
 
             check_types_and_consts(
                 expected_min,
index e007d971bb072bd1ae109b0c70fca6803e4915b6..4ffb061f7b48e8eddec8e1b19f492ee7ad90e03e 100644 (file)
@@ -356,6 +356,7 @@ fn confirm_builtin_call(
                     }
                 }
 
+                let callee_ty = self.resolve_vars_if_possible(callee_ty);
                 let mut err = type_error_struct!(
                     self.tcx.sess,
                     callee_expr.span,
index 9f0ed0cd18d920aeeb125d9f5bcd4ed3902cdd21..c1d14413554ac71e3b829e3b5961f7eb62fdff4d 100644 (file)
@@ -70,6 +70,7 @@ pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ab
 ///
 /// * ...
 /// * inherited: other fields inherited from the enclosing fn (if any)
+#[instrument(skip(inherited, body), level = "debug")]
 pub(super) fn check_fn<'a, 'tcx>(
     inherited: &'a Inherited<'a, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -82,8 +83,6 @@ pub(super) fn check_fn<'a, 'tcx>(
 ) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) {
     let mut fn_sig = fn_sig;
 
-    debug!("check_fn(sig={:?}, fn_id={}, param_env={:?})", fn_sig, fn_id, param_env);
-
     // Create the function context. This is either derived from scratch or,
     // in the case of closures, based on the outer context.
     let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id);
index 65ba1c08b6243fa36c1b751cdc19f62253a37a23..410ac24b1f19c84a8659e5186f31f5c264db201d 100644 (file)
@@ -33,6 +33,7 @@ struct ClosureSignatures<'tcx> {
 }
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+    #[instrument(skip(self, expr, _capture, decl, body_id), level = "debug")]
     pub fn check_expr_closure(
         &self,
         expr: &hir::Expr<'_>,
@@ -42,7 +43,8 @@ pub fn check_expr_closure(
         gen: Option<hir::Movability>,
         expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
-        debug!("check_expr_closure(expr={:?},expected={:?})", expr, expected);
+        trace!("decl = {:#?}", decl);
+        trace!("expr = {:#?}", expr);
 
         // It's always helpful for inference if we know the kind of
         // closure sooner rather than later, so first examine the expected
@@ -55,6 +57,7 @@ pub fn check_expr_closure(
         self.check_closure(expr, expected_kind, decl, body, gen, expected_sig)
     }
 
+    #[instrument(skip(self, expr, body, decl), level = "debug")]
     fn check_closure(
         &self,
         expr: &hir::Expr<'_>,
@@ -64,14 +67,14 @@ fn check_closure(
         gen: Option<hir::Movability>,
         expected_sig: Option<ExpectedSig<'tcx>>,
     ) -> Ty<'tcx> {
-        debug!("check_closure(opt_kind={:?}, expected_sig={:?})", opt_kind, expected_sig);
-
+        trace!("decl = {:#?}", decl);
         let expr_def_id = self.tcx.hir().local_def_id(expr.hir_id);
+        debug!(?expr_def_id);
 
         let ClosureSignatures { bound_sig, liberated_sig } =
             self.sig_of_closure(expr.hir_id, expr_def_id.to_def_id(), decl, body, expected_sig);
 
-        debug!("check_closure: ty_of_closure returns {:?}", liberated_sig);
+        debug!(?bound_sig, ?liberated_sig);
 
         let return_type_pre_known = !liberated_sig.output().is_ty_infer();
 
@@ -130,10 +133,7 @@ fn check_closure(
             )
         });
 
-        debug!(
-            "check_closure: expr_def_id={:?}, sig={:?}, opt_kind={:?}",
-            expr_def_id, sig, opt_kind
-        );
+        debug!(?sig, ?opt_kind);
 
         let closure_kind_ty = match opt_kind {
             Some(kind) => kind.to_ty(self.tcx),
@@ -159,19 +159,18 @@ fn check_closure(
 
         let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs);
 
-        debug!("check_closure: expr.hir_id={:?} closure_type={:?}", expr.hir_id, closure_type);
+        debug!(?expr.hir_id, ?closure_type);
 
         closure_type
     }
 
     /// Given the expected type, figures out what it can about this closure we
     /// are about to type check:
+    #[instrument(skip(self), level = "debug")]
     fn deduce_expectations_from_expected_type(
         &self,
         expected_ty: Ty<'tcx>,
     ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
-        debug!("deduce_expectations_from_expected_type(expected_ty={:?})", expected_ty);
-
         match *expected_ty.kind() {
             ty::Dynamic(ref object_type, ..) => {
                 let sig = object_type.projection_bounds().find_map(|pb| {
@@ -314,6 +313,7 @@ fn sig_of_closure(
 
     /// If there is no expected signature, then we will convert the
     /// types that the user gave into a signature.
+    #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")]
     fn sig_of_closure_no_expectation(
         &self,
         hir_id: hir::HirId,
@@ -321,8 +321,6 @@ fn sig_of_closure_no_expectation(
         decl: &hir::FnDecl<'_>,
         body: &hir::Body<'_>,
     ) -> ClosureSignatures<'tcx> {
-        debug!("sig_of_closure_no_expectation()");
-
         let bound_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body);
 
         self.closure_sigs(expr_def_id, body, bound_sig)
@@ -375,6 +373,7 @@ fn sig_of_closure_no_expectation(
     /// - `expected_sig`: the expected signature (if any). Note that
     ///   this is missing a binder: that is, there may be late-bound
     ///   regions with depth 1, which are bound then by the closure.
+    #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")]
     fn sig_of_closure_with_expectation(
         &self,
         hir_id: hir::HirId,
@@ -383,8 +382,6 @@ fn sig_of_closure_with_expectation(
         body: &hir::Body<'_>,
         expected_sig: ExpectedSig<'tcx>,
     ) -> ClosureSignatures<'tcx> {
-        debug!("sig_of_closure_with_expectation(expected_sig={:?})", expected_sig);
-
         // Watch out for some surprises and just ignore the
         // expectation if things don't see to match up with what we
         // expect.
@@ -553,6 +550,7 @@ fn check_supplied_sig_against_expectation(
     /// types that the user gave into a signature.
     ///
     /// Also, record this closure signature for later.
+    #[instrument(skip(self, decl, body), level = "debug")]
     fn supplied_sig_of_closure(
         &self,
         hir_id: hir::HirId,
@@ -562,10 +560,8 @@ fn supplied_sig_of_closure(
     ) -> ty::PolyFnSig<'tcx> {
         let astconv: &dyn AstConv<'_> = self;
 
-        debug!(
-            "supplied_sig_of_closure(decl={:?}, body.generator_kind={:?})",
-            decl, body.generator_kind,
-        );
+        trace!("decl = {:#?}", decl);
+        debug!(?body.generator_kind);
 
         let bound_vars = self.tcx.late_bound_vars(hir_id);
 
@@ -578,7 +574,7 @@ fn supplied_sig_of_closure(
                 // we expect the return type of the block to match that of the enclosing
                 // function.
                 Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => {
-                    debug!("supplied_sig_of_closure: closure is async fn body");
+                    debug!("closure is async fn body");
                     self.deduce_future_output_from_obligations(expr_def_id).unwrap_or_else(|| {
                         // AFAIK, deducing the future output
                         // always succeeds *except* in error cases
@@ -606,7 +602,7 @@ fn supplied_sig_of_closure(
             bound_vars,
         );
 
-        debug!("supplied_sig_of_closure: result={:?}", result);
+        debug!(?result);
 
         let c_result = self.inh.infcx.canonicalize_response(result);
         self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result);
index ef18c1e7779d862a4a9710eb6cb1c80a98bb02b1..79763942d05d96f6ce3011f5f1ae0ac733ba67d3 100644 (file)
@@ -482,12 +482,11 @@ fn coerce_borrowed_pointer(
     // &[T; n] or &mut [T; n] -> &[T]
     // or &mut [T; n] -> &mut [T]
     // or &Concrete -> &Trait, etc.
+    #[instrument(skip(self), level = "debug")]
     fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> {
-        debug!("coerce_unsized(source={:?}, target={:?})", source, target);
-
         source = self.shallow_resolve(source);
         target = self.shallow_resolve(target);
-        debug!("coerce_unsized: resolved source={:?} target={:?}", source, target);
+        debug!(?source, ?target);
 
         // These 'if' statements require some explanation.
         // The `CoerceUnsized` trait is special - it is only
index 722b110ed61084f5b0454bba56be11566a59d719..dcfbaff7ec792f74935e91559725837b5777580f 100644 (file)
@@ -59,6 +59,7 @@ pub fn demand_suptype_diag(
         self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub fn demand_suptype_with_origin(
         &self,
         cause: &ObligationCause<'tcx>,
index 8a69e0a737d501c1b2aed4d780c1a65fd8b321da..09a55937cc5adfb77688f40195e946265a8fad4c 100644 (file)
@@ -156,7 +156,7 @@ pub(super) fn check_expr_with_needs(
     /// Note that inspecting a type's structure *directly* may expose the fact
     /// that there are actually multiple representations for `Error`, so avoid
     /// that when err needs to be handled differently.
-    #[instrument(skip(self), level = "debug")]
+    #[instrument(skip(self, expr), level = "debug")]
     pub(super) fn check_expr_with_expectation(
         &self,
         expr: &'tcx hir::Expr<'tcx>,
@@ -254,12 +254,13 @@ pub(super) fn check_expr_with_expectation_and_args(
         ty
     }
 
+    #[instrument(skip(self, expr), level = "debug")]
     fn check_expr_kind(
         &self,
         expr: &'tcx hir::Expr<'tcx>,
         expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
-        debug!("check_expr_kind(expected={:?}, expr={:?})", expected, expr);
+        trace!("expr={:#?}", expr);
 
         let tcx = self.tcx;
         match expr.kind {
@@ -2136,7 +2137,7 @@ fn check_expr_index(
             idx_t
         } else {
             let base_t = self.structurally_resolved_type(base.span, base_t);
-            match self.lookup_indexing(expr, base, base_t, idx_t) {
+            match self.lookup_indexing(expr, base, base_t, idx, idx_t) {
                 Some((index_ty, element_ty)) => {
                     // two-phase not needed because index_ty is never mutable
                     self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
index 553f5ed8a0ca4f358cf9c59e1b7241137652dd0b..7b9629e534bf92348446f6ce5f9d5d73af35ce34 100644 (file)
@@ -87,23 +87,22 @@ pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty
         self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
     }
 
+    #[instrument(skip(self, mutate_fulfillment_errors), level = "debug")]
     pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment(
         &self,
         mut ty: Ty<'tcx>,
         mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
     ) -> Ty<'tcx> {
-        debug!("resolve_vars_with_obligations(ty={:?})", ty);
-
         // No Infer()? Nothing needs doing.
         if !ty.has_infer_types_or_consts() {
-            debug!("resolve_vars_with_obligations: ty={:?}", ty);
+            debug!("no inference var, nothing needs doing");
             return ty;
         }
 
         // If `ty` is a type variable, see whether we already know what it is.
         ty = self.resolve_vars_if_possible(ty);
         if !ty.has_infer_types_or_consts() {
-            debug!("resolve_vars_with_obligations: ty={:?}", ty);
+            debug!(?ty);
             return ty;
         }
 
@@ -114,7 +113,7 @@ pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment(
         self.select_obligations_where_possible(false, mutate_fulfillment_errors);
         ty = self.resolve_vars_if_possible(ty);
 
-        debug!("resolve_vars_with_obligations: ty={:?}", ty);
+        debug!(?ty);
         ty
     }
 
@@ -230,6 +229,7 @@ pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) {
     /// This should be invoked **before any unifications have
     /// occurred**, so that annotations like `Vec<_>` are preserved
     /// properly.
+    #[instrument(skip(self), level = "debug")]
     pub fn write_user_type_annotation_from_substs(
         &self,
         hir_id: hir::HirId,
@@ -237,37 +237,25 @@ pub fn write_user_type_annotation_from_substs(
         substs: SubstsRef<'tcx>,
         user_self_ty: Option<UserSelfTy<'tcx>>,
     ) {
-        debug!(
-            "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \
-             user_self_ty={:?} in fcx {}",
-            hir_id,
-            def_id,
-            substs,
-            user_self_ty,
-            self.tag(),
-        );
+        debug!("fcx {}", self.tag());
 
         if self.can_contain_user_lifetime_bounds((substs, user_self_ty)) {
             let canonicalized = self.infcx.canonicalize_user_type_annotation(UserType::TypeOf(
                 def_id,
                 UserSubsts { substs, user_self_ty },
             ));
-            debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized);
+            debug!(?canonicalized);
             self.write_user_type_annotation(hir_id, canonicalized);
         }
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub fn write_user_type_annotation(
         &self,
         hir_id: hir::HirId,
         canonical_user_type_annotation: CanonicalUserType<'tcx>,
     ) {
-        debug!(
-            "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}",
-            hir_id,
-            canonical_user_type_annotation,
-            self.tag(),
-        );
+        debug!("fcx {}", self.tag());
 
         if !canonical_user_type_annotation.is_identity() {
             self.typeck_results
@@ -275,12 +263,13 @@ pub fn write_user_type_annotation(
                 .user_provided_types_mut()
                 .insert(hir_id, canonical_user_type_annotation);
         } else {
-            debug!("write_user_type_annotation: skipping identity substs");
+            debug!("skipping identity substs");
         }
     }
 
+    #[instrument(skip(self, expr), level = "debug")]
     pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) {
-        debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
+        debug!("expr = {:#?}", expr);
 
         if adj.is_empty() {
             return;
@@ -652,8 +641,8 @@ pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
         }
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub(in super::super) fn select_all_obligations_or_error(&self) {
-        debug!("select_all_obligations_or_error");
         if let Err(errors) = self
             .fulfillment_cx
             .borrow_mut()
@@ -694,16 +683,15 @@ pub(in super::super) fn make_overloaded_place_return_type(
         ret_ty.builtin_deref(true).unwrap()
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn self_type_matches_expected_vid(
         &self,
         trait_ref: ty::PolyTraitRef<'tcx>,
         expected_vid: ty::TyVid,
     ) -> bool {
         let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty());
-        debug!(
-            "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
-            trait_ref, self_ty, expected_vid
-        );
+        debug!(?self_ty);
+
         match *self_ty.kind() {
             ty::Infer(ty::TyVar(found_vid)) => {
                 // FIXME: consider using `sub_root_var` here so we
@@ -716,6 +704,7 @@ fn self_type_matches_expected_vid(
         }
     }
 
+    #[instrument(skip(self), level = "debug")]
     pub(in super::super) fn obligations_for_self_ty<'b>(
         &'b self,
         self_ty: ty::TyVid,
@@ -725,12 +714,7 @@ pub(in super::super) fn obligations_for_self_ty<'b>(
         // FIXME: consider using `sub_root_var` here so we
         // can see through subtyping.
         let ty_var_root = self.root_var(self_ty);
-        debug!(
-            "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
-            self_ty,
-            ty_var_root,
-            self.fulfillment_cx.borrow().pending_obligations()
-        );
+        trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations());
 
         self.fulfillment_cx
             .borrow()
@@ -780,6 +764,7 @@ pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
 
     /// Unifies the output type with the expected type early, for more coercions
     /// and forward type information on the input expressions.
+    #[instrument(skip(self, call_span), level = "debug")]
     pub(in super::super) fn expected_inputs_for_expected_output(
         &self,
         call_span: Span,
@@ -826,10 +811,7 @@ pub(in super::super) fn expected_inputs_for_expected_output(
                 Ok(formal_args.iter().map(|&ty| self.resolve_vars_if_possible(ty)).collect())
             })
             .unwrap_or_default();
-        debug!(
-            "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})",
-            formal_args, formal_ret, expect_args, expected_ret
-        );
+        debug!(?formal_args, ?formal_ret, ?expect_args, ?expected_ret);
         expect_args
     }
 
@@ -1195,6 +1177,7 @@ pub(in super::super) fn could_remove_semicolon(
 
     // Instantiates the given path, which must refer to an item with the given
     // number of type parameters and type.
+    #[instrument(skip(self, span), level = "debug")]
     pub fn instantiate_value_path(
         &self,
         segments: &[hir::PathSegment<'_>],
@@ -1203,11 +1186,6 @@ pub fn instantiate_value_path(
         span: Span,
         hir_id: hir::HirId,
     ) -> (Ty<'tcx>, Res) {
-        debug!(
-            "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})",
-            segments, self_ty, res, hir_id,
-        );
-
         let tcx = self.tcx;
 
         let path_segs = match res {
@@ -1230,7 +1208,7 @@ pub fn instantiate_value_path(
             }
             Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => {
                 let container = tcx.associated_item(def_id).container;
-                debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
+                debug!(?def_id, ?container);
                 match container {
                     ty::TraitContainer(trait_did) => {
                         callee::check_legal_trait_for_method_call(tcx, span, None, span, trait_did)
index 91a164ce063ea6cae022e8254e572e8149338bb9..9c7853dd78d3bbb61fd295857dff464714a0f7ba 100644 (file)
@@ -176,6 +176,7 @@ pub fn report_method_error(
                                 sugg_span,
                                 idx,
                                 self.tcx.sess.source_map(),
+                                item.fn_has_self_parameter,
                             );
                         }
                     }
@@ -218,6 +219,7 @@ pub fn report_method_error(
                             sugg_span,
                             idx,
                             self.tcx.sess.source_map(),
+                            item.fn_has_self_parameter,
                         );
                     }
                 }
@@ -1736,6 +1738,7 @@ fn print_disambiguation_help(
     span: Span,
     candidate: Option<usize>,
     source_map: &source_map::SourceMap,
+    fn_has_self_parameter: bool,
 ) {
     let mut applicability = Applicability::MachineApplicable;
     let (span, sugg) = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) {
@@ -1754,9 +1757,14 @@ fn print_disambiguation_help(
                 .collect::<Vec<_>>()
                 .join(", "),
         );
+        let trait_name = if !fn_has_self_parameter {
+            format!("<{} as {}>", rcvr_ty, trait_name)
+        } else {
+            trait_name
+        };
         (span, format!("{}::{}{}", trait_name, item_name, args))
     } else {
-        (span.with_hi(item_name.span.lo()), format!("{}::", trait_name))
+        (span.with_hi(item_name.span.lo()), format!("<{} as {}>::", rcvr_ty, trait_name))
     };
     err.span_suggestion_verbose(
         span,
index 055072d3a1d9d4a9cf020d6d7f999592b7029091..64775d7aba9f8688dfad292fe550a80f9eb69f46 100644 (file)
@@ -1,5 +1,7 @@
 use crate::check::method::MethodCallee;
 use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp};
+use rustc_ast as ast;
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::InferOk;
@@ -47,6 +49,7 @@ pub(super) fn lookup_indexing(
         expr: &hir::Expr<'_>,
         base_expr: &'tcx hir::Expr<'tcx>,
         base_ty: Ty<'tcx>,
+        index_expr: &'tcx hir::Expr<'tcx>,
         idx_ty: Ty<'tcx>,
     ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
         // FIXME(#18741) -- this is almost but not quite the same as the
@@ -56,12 +59,42 @@ pub(super) fn lookup_indexing(
         let mut autoderef = self.autoderef(base_expr.span, base_ty);
         let mut result = None;
         while result.is_none() && autoderef.next().is_some() {
-            result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
+            result = self.try_index_step(expr, base_expr, &autoderef, idx_ty, index_expr);
         }
         self.register_predicates(autoderef.into_obligations());
         result
     }
 
+    fn negative_index(
+        &self,
+        ty: Ty<'tcx>,
+        span: Span,
+        base_expr: &hir::Expr<'_>,
+    ) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
+        let ty = self.resolve_vars_if_possible(ty);
+        let mut err = self.tcx.sess.struct_span_err(
+            span,
+            &format!("negative integers cannot be used to index on a `{}`", ty),
+        );
+        err.span_label(span, &format!("cannot use a negative integer for indexing on `{}`", ty));
+        if let (hir::ExprKind::Path(..), Ok(snippet)) =
+            (&base_expr.kind, self.tcx.sess.source_map().span_to_snippet(base_expr.span))
+        {
+            // `foo[-1]` to `foo[foo.len() - 1]`
+            err.span_suggestion_verbose(
+                span.shrink_to_lo(),
+                &format!(
+                    "to access an element starting from the end of the `{}`, compute the index",
+                    ty,
+                ),
+                format!("{}.len() ", snippet),
+                Applicability::MachineApplicable,
+            );
+        }
+        err.emit();
+        Some((self.tcx.ty_error(), self.tcx.ty_error()))
+    }
+
     /// To type-check `base_expr[index_expr]`, we progressively autoderef
     /// (and otherwise adjust) `base_expr`, looking for a type which either
     /// supports builtin indexing or overloaded indexing.
@@ -73,6 +106,7 @@ fn try_index_step(
         base_expr: &hir::Expr<'_>,
         autoderef: &Autoderef<'a, 'tcx>,
         index_ty: Ty<'tcx>,
+        index_expr: &hir::Expr<'_>,
     ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
         let adjusted_ty =
             self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
@@ -82,6 +116,27 @@ fn try_index_step(
             expr, base_expr, adjusted_ty, index_ty
         );
 
+        if let hir::ExprKind::Unary(
+            hir::UnOp::Neg,
+            hir::Expr {
+                kind: hir::ExprKind::Lit(hir::Lit { node: ast::LitKind::Int(..), .. }),
+                ..
+            },
+        ) = index_expr.kind
+        {
+            match adjusted_ty.kind() {
+                ty::Adt(ty::AdtDef { did, .. }, _)
+                    if self.tcx.is_diagnostic_item(sym::vec_type, *did) =>
+                {
+                    return self.negative_index(adjusted_ty, index_expr.span, base_expr);
+                }
+                ty::Slice(_) | ty::Array(_, _) => {
+                    return self.negative_index(adjusted_ty, index_expr.span, base_expr);
+                }
+                _ => {}
+            }
+        }
+
         for unsize in [false, true] {
             let mut self_ty = adjusted_ty;
             if unsize {
index 917bf4ecd8c4a9117a5e80a6421beadb7836d0e4..1f2ccffa6f247f909c88b9220792f02777bb473b 100644 (file)
@@ -122,6 +122,7 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Analysis starting point.
+    #[instrument(skip(self, body), level = "debug")]
     fn analyze_closure(
         &self,
         closure_hir_id: hir::HirId,
@@ -130,8 +131,6 @@ fn analyze_closure(
         body: &'tcx hir::Body<'tcx>,
         capture_clause: hir::CaptureBy,
     ) {
-        debug!("analyze_closure(id={:?}, body.id={:?})", closure_hir_id, body.id());
-
         // Extract the type of the closure.
         let ty = self.node_ty(closure_hir_id);
         let (closure_def_id, substs) = match *ty.kind() {
@@ -1683,15 +1682,12 @@ struct InferBorrowKind<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
+    #[instrument(skip(self), level = "debug")]
     fn adjust_upvar_borrow_kind_for_consume(
         &mut self,
         place_with_id: &PlaceWithHirId<'tcx>,
         diag_expr_id: hir::HirId,
     ) {
-        debug!(
-            "adjust_upvar_borrow_kind_for_consume(place_with_id={:?}, diag_expr_id={:?})",
-            place_with_id, diag_expr_id
-        );
         let tcx = self.fcx.tcx;
         let upvar_id = if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
             upvar_id
@@ -1699,7 +1695,7 @@ fn adjust_upvar_borrow_kind_for_consume(
             return;
         };
 
-        debug!("adjust_upvar_borrow_kind_for_consume: upvar={:?}", upvar_id);
+        debug!(?upvar_id);
 
         let usage_span = tcx.hir().span(diag_expr_id);
 
@@ -1718,16 +1714,12 @@ fn adjust_upvar_borrow_kind_for_consume(
     /// Indicates that `place_with_id` is being directly mutated (e.g., assigned
     /// to). If the place is based on a by-ref upvar, this implies that
     /// the upvar must be borrowed using an `&mut` borrow.
+    #[instrument(skip(self), level = "debug")]
     fn adjust_upvar_borrow_kind_for_mut(
         &mut self,
         place_with_id: &PlaceWithHirId<'tcx>,
         diag_expr_id: hir::HirId,
     ) {
-        debug!(
-            "adjust_upvar_borrow_kind_for_mut(place_with_id={:?}, diag_expr_id={:?})",
-            place_with_id, diag_expr_id
-        );
-
         if let PlaceBase::Upvar(_) = place_with_id.place.base {
             // Raw pointers don't inherit mutability
             if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
@@ -1737,16 +1729,12 @@ fn adjust_upvar_borrow_kind_for_mut(
         }
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn adjust_upvar_borrow_kind_for_unique(
         &mut self,
         place_with_id: &PlaceWithHirId<'tcx>,
         diag_expr_id: hir::HirId,
     ) {
-        debug!(
-            "adjust_upvar_borrow_kind_for_unique(place_with_id={:?}, diag_expr_id={:?})",
-            place_with_id, diag_expr_id
-        );
-
         if let PlaceBase::Upvar(_) = place_with_id.place.base {
             if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
                 // Raw pointers don't inherit mutability.
@@ -1783,6 +1771,7 @@ fn adjust_upvar_deref(
     /// moving from left to right as needed (but never right to left).
     /// Here the argument `mutbl` is the borrow_kind that is required by
     /// some particular use.
+    #[instrument(skip(self), level = "debug")]
     fn adjust_upvar_borrow_kind(
         &mut self,
         place_with_id: &PlaceWithHirId<'tcx>,
@@ -1791,10 +1780,7 @@ fn adjust_upvar_borrow_kind(
     ) {
         let curr_capture_info = self.capture_information[&place_with_id.place];
 
-        debug!(
-            "adjust_upvar_borrow_kind(place={:?}, diag_expr_id={:?}, capture_info={:?}, kind={:?})",
-            place_with_id, diag_expr_id, curr_capture_info, kind
-        );
+        debug!(?curr_capture_info);
 
         if let ty::UpvarCapture::ByValue(_) = curr_capture_info.capture_kind {
             // It's already captured by value, we don't need to do anything here
@@ -1814,6 +1800,7 @@ fn adjust_upvar_borrow_kind(
         };
     }
 
+    #[instrument(skip(self, diag_expr_id), level = "debug")]
     fn init_capture_info_for_place(
         &mut self,
         place_with_id: &PlaceWithHirId<'tcx>,
@@ -1840,7 +1827,7 @@ fn init_capture_info_for_place(
 
             self.capture_information.insert(place_with_id.place.clone(), capture_info);
         } else {
-            debug!("Not upvar: {:?}", place_with_id);
+            debug!("Not upvar");
         }
     }
 }
@@ -1867,9 +1854,8 @@ fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id:
         }
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
-        debug!("consume(place_with_id={:?}, diag_expr_id={:?})", place_with_id, diag_expr_id);
-
         if !self.capture_information.contains_key(&place_with_id.place) {
             self.init_capture_info_for_place(&place_with_id, diag_expr_id);
         }
@@ -1877,17 +1863,13 @@ fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::H
         self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id);
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn borrow(
         &mut self,
         place_with_id: &PlaceWithHirId<'tcx>,
         diag_expr_id: hir::HirId,
         bk: ty::BorrowKind,
     ) {
-        debug!(
-            "borrow(place_with_id={:?}, diag_expr_id={:?}, bk={:?})",
-            place_with_id, diag_expr_id, bk
-        );
-
         // The region here will get discarded/ignored
         let dummy_capture_kind =
             ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: bk, region: &ty::ReErased });
@@ -1924,9 +1906,8 @@ fn borrow(
         }
     }
 
+    #[instrument(skip(self), level = "debug")]
     fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
-        debug!("mutate(assignee_place={:?}, diag_expr_id={:?})", assignee_place, diag_expr_id);
-
         self.borrow(assignee_place, diag_expr_id, ty::BorrowKind::MutBorrow);
     }
 }
index c57ec9ef78f6856804cbe95c990e5b3b6a56241e..2c0d926dd015e7e1ad831957488c4de15c08bf60 100644 (file)
@@ -497,6 +497,7 @@ fn visit_generator_interior_types(&mut self) {
             fcx_typeck_results.generator_interior_types.clone();
     }
 
+    #[instrument(skip(self, span), level = "debug")]
     fn visit_opaque_types(&mut self, span: Span) {
         let opaque_types = self.fcx.infcx.inner.borrow().opaque_types.clone();
         for (opaque_type_key, opaque_defn) in opaque_types {
@@ -564,6 +565,7 @@ fn visit_field_id(&mut self, hir_id: hir::HirId) {
         }
     }
 
+    #[instrument(skip(self, span), level = "debug")]
     fn visit_node_id(&mut self, span: Span, hir_id: hir::HirId) {
         // Export associated path extensions and method resolutions.
         if let Some(def) =
@@ -579,7 +581,7 @@ fn visit_node_id(&mut self, span: Span, hir_id: hir::HirId) {
         let n_ty = self.fcx.node_ty(hir_id);
         let n_ty = self.resolve(n_ty, &span);
         self.write_ty_to_typeck_results(hir_id, n_ty);
-        debug!("node {:?} has type {:?}", hir_id, n_ty);
+        debug!(?n_ty);
 
         // Resolve any substitutions
         if let Some(substs) = self.fcx.typeck_results.borrow().node_substs_opt(hir_id) {
@@ -590,31 +592,33 @@ fn visit_node_id(&mut self, span: Span, hir_id: hir::HirId) {
         }
     }
 
+    #[instrument(skip(self, span), level = "debug")]
     fn visit_adjustments(&mut self, span: Span, hir_id: hir::HirId) {
         let adjustment = self.fcx.typeck_results.borrow_mut().adjustments_mut().remove(hir_id);
         match adjustment {
             None => {
-                debug!("no adjustments for node {:?}", hir_id);
+                debug!("no adjustments for node");
             }
 
             Some(adjustment) => {
                 let resolved_adjustment = self.resolve(adjustment, &span);
-                debug!("adjustments for node {:?}: {:?}", hir_id, resolved_adjustment);
+                debug!(?resolved_adjustment);
                 self.typeck_results.adjustments_mut().insert(hir_id, resolved_adjustment);
             }
         }
     }
 
+    #[instrument(skip(self, span), level = "debug")]
     fn visit_pat_adjustments(&mut self, span: Span, hir_id: hir::HirId) {
         let adjustment = self.fcx.typeck_results.borrow_mut().pat_adjustments_mut().remove(hir_id);
         match adjustment {
             None => {
-                debug!("no pat_adjustments for node {:?}", hir_id);
+                debug!("no pat_adjustments for node");
             }
 
             Some(adjustment) => {
                 let resolved_adjustment = self.resolve(adjustment, &span);
-                debug!("pat_adjustments for node {:?}: {:?}", hir_id, resolved_adjustment);
+                debug!(?resolved_adjustment);
                 self.typeck_results.pat_adjustments_mut().insert(hir_id, resolved_adjustment);
             }
         }
@@ -732,6 +736,26 @@ fn report_const_error(&self, c: &'tcx ty::Const<'tcx>) {
     }
 }
 
+struct EraseEarlyRegions<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> TypeFolder<'tcx> for EraseEarlyRegions<'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if ty.has_type_flags(ty::TypeFlags::HAS_POTENTIAL_FREE_REGIONS) {
+            ty.super_fold_with(self)
+        } else {
+            ty
+        }
+    }
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        if let ty::ReLateBound(..) = r { r } else { self.tcx.lifetimes.re_erased }
+    }
+}
+
 impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> {
     fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
         self.tcx
@@ -739,7 +763,13 @@ fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
         match self.infcx.fully_resolve(t) {
-            Ok(t) => self.infcx.tcx.erase_regions(t),
+            Ok(t) => {
+                // Do not anonymize late-bound regions
+                // (e.g. keep `for<'a>` named `for<'a>`).
+                // This allows NLL to generate error messages that
+                // refer to the higher-ranked lifetime names written by the user.
+                EraseEarlyRegions { tcx: self.infcx.tcx }.fold_ty(t)
+            }
             Err(_) => {
                 debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t);
                 self.report_type_error(t);
index ba70006fe96b3bb7a918bbcde28f049addbd7ec0..b5c4d6ac261670f7905fe16d521505bdeb544971 100644 (file)
@@ -619,6 +619,8 @@ fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) {
 
         if let Some(hir::Guard::If(ref e)) = arm.guard {
             self.consume_expr(e)
+        } else if let Some(hir::Guard::IfLet(_, ref e)) = arm.guard {
+            self.consume_expr(e)
         }
 
         self.consume_expr(&arm.body);
index 2e3db4d6d655f53447438c62ec0c76ad90b7a397..c2038f4e047ce96cdd335039c6822df9dd444fdd 100644 (file)
@@ -731,7 +731,11 @@ fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) {
     /// Builds the `type defined here` message.
     fn show_definition(&self, err: &mut DiagnosticBuilder<'_>) {
         let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
-            def_span.into()
+            if self.tcx.sess.source_map().span_to_snippet(def_span).is_ok() {
+                def_span.into()
+            } else {
+                return;
+            }
         } else {
             return;
         };
index e56b631dbaf8db537afb2bd8196e709ba074f396..c0121eebb7feff689395915f461e33529483790c 100644 (file)
@@ -1303,6 +1303,11 @@ fn clone(&self) -> Self {
 ///
 /// See the [module-level documentation](self) for more.
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(
+    not(bootstrap),
+    must_not_suspend = "Holding a Ref across suspend \
+                      points can cause BorrowErrors"
+)]
 pub struct Ref<'b, T: ?Sized + 'b> {
     value: &'b T,
     borrow: BorrowRef<'b>,
@@ -1679,6 +1684,11 @@ fn clone(&self) -> BorrowRefMut<'b> {
 ///
 /// See the [module-level documentation](self) for more.
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(
+    not(bootstrap),
+    must_not_suspend = "Holding a RefMut across suspend \
+                      points can cause BorrowErrors"
+)]
 pub struct RefMut<'b, T: ?Sized + 'b> {
     value: &'b mut T,
     borrow: BorrowRefMut<'b>,
index 1e512af48051ed9dc7c5627ed29aef8b0906506f..72dbe845c97a9f08ca026bd70a1853ab9daa7370 100644 (file)
@@ -532,9 +532,10 @@ fn as_mut(&mut self) -> &mut U {
 
 // From implies Into
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<T, U> Into<U> for T
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T, U> const Into<U> for T
 where
-    U: From<T>,
+    U: ~const From<T>,
 {
     fn into(self) -> U {
         U::from(self)
@@ -543,7 +544,8 @@ fn into(self) -> U {
 
 // From (and thus Into) is reflexive
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<T> From<T> for T {
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T> const From<T> for T {
     fn from(t: T) -> T {
         t
     }
index f7ed811e4473329fd3ea58df4afa3523715e0514..0f835689699fc110626c214a5a565396669a7f59 100644 (file)
@@ -672,6 +672,11 @@ fn max(mut self) -> Option<A> {
         self.next_back()
     }
 
+    #[inline]
+    fn is_sorted(self) -> bool {
+        true
+    }
+
     #[inline]
     #[doc(hidden)]
     unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
@@ -1095,6 +1100,11 @@ fn min(mut self) -> Option<A> {
     fn max(mut self) -> Option<A> {
         self.next_back()
     }
+
+    #[inline]
+    fn is_sorted(self) -> bool {
+        true
+    }
 }
 
 #[stable(feature = "inclusive_range", since = "1.26.0")]
index 265ba9f1bb91b69d1fc9c2b162b4a18a03db34d9..4408b5a3d2088687a1244908a842d65f80162a89 100644 (file)
@@ -82,6 +82,7 @@
 #![feature(const_float_bits_conv)]
 #![feature(const_float_classify)]
 #![feature(const_heap)]
+#![feature(const_convert)]
 #![feature(const_inherent_unchecked_arith)]
 #![feature(const_int_unchecked_arith)]
 #![feature(const_intrinsic_copy)]
 #![feature(link_llvm_intrinsics)]
 #![feature(llvm_asm)]
 #![feature(min_specialization)]
+#![cfg_attr(not(bootstrap), feature(must_not_suspend))]
 #![feature(negative_impls)]
 #![feature(never_type)]
 #![feature(no_core)]
index 94d892dd787a6874579ae7db1e405a5a1efbfcc6..a162c0340fb24144a9f82238c4e4eeb499545532 100644 (file)
@@ -2019,7 +2019,8 @@ fn from_iter<I: IntoIterator<Item = Option<A>>>(iter: I) -> Option<V> {
 }
 
 #[unstable(feature = "try_trait_v2", issue = "84277")]
-impl<T> ops::Try for Option<T> {
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T> const ops::Try for Option<T> {
     type Output = T;
     type Residual = Option<convert::Infallible>;
 
@@ -2038,7 +2039,8 @@ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
 }
 
 #[unstable(feature = "try_trait_v2", issue = "84277")]
-impl<T> ops::FromResidual for Option<T> {
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T> const ops::FromResidual for Option<T> {
     #[inline]
     fn from_residual(residual: Option<convert::Infallible>) -> Self {
         match residual {
index 4a300f857e9ed9dd4623ca07c983dc6f0f5b6cb5..bd4e623732e2a206ff887e28ea6b9d02e8d30ab3 100644 (file)
@@ -1889,7 +1889,8 @@ fn from_iter<I: IntoIterator<Item = Result<A, E>>>(iter: I) -> Result<V, E> {
 }
 
 #[unstable(feature = "try_trait_v2", issue = "84277")]
-impl<T, E> ops::Try for Result<T, E> {
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T, E> const ops::Try for Result<T, E> {
     type Output = T;
     type Residual = Result<convert::Infallible, E>;
 
@@ -1908,7 +1909,10 @@ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
 }
 
 #[unstable(feature = "try_trait_v2", issue = "84277")]
-impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Result<T, F> {
+#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
+impl<T, E, F: ~const From<E>> const ops::FromResidual<Result<convert::Infallible, E>>
+    for Result<T, F>
+{
     #[inline]
     fn from_residual(residual: Result<convert::Infallible, E>) -> Self {
         match residual {
index 8a31388fbdbbc6b68704022a2de0f4e09d50f5b6..60b39295cafe1e26f7ea3517a415bec85493e68d 100644 (file)
@@ -6,8 +6,6 @@
 //! Unstable sorting is compatible with libcore because it doesn't allocate memory, unlike our
 //! stable sorting implementation.
 
-// ignore-tidy-undocumented-unsafe
-
 use crate::cmp;
 use crate::mem::{self, MaybeUninit};
 use crate::ptr;
@@ -291,6 +289,9 @@ fn width<T>(l: *mut T, r: *mut T) -> usize {
             } else if start_r < end_r {
                 block_l = rem;
             } else {
+                // There were the same number of elements to switch on both blocks during the last
+                // iteration, so there are no remaining elements on either block. Cover the remaining
+                // items with roughly equally-sized blocks.
                 block_l = rem / 2;
                 block_r = rem - block_l;
             }
@@ -437,6 +438,17 @@ macro_rules! right {
         // Move its remaining out-of-order elements to the far right.
         debug_assert_eq!(width(l, r), block_l);
         while start_l < end_l {
+            // remaining-elements-safety
+            // SAFETY: while the loop condition holds there are still elements in `offsets_l`, so it
+            // is safe to point `end_l` to the previous element.
+            //
+            // The `ptr::swap` is safe if both its arguments are valid for reads and writes:
+            //  - Per the debug assert above, the distance between `l` and `r` is `block_l`
+            //    elements, so there can be at most `block_l` remaining offsets between `start_l`
+            //    and `end_l`. This means `r` will be moved at most `block_l` steps back, which
+            //    makes the `r.offset` calls valid (at that point `l == r`).
+            //  - `offsets_l` contains valid offsets into `v` collected during the partitioning of
+            //    the last block, so the `l.offset` calls are valid.
             unsafe {
                 end_l = end_l.offset(-1);
                 ptr::swap(l.offset(*end_l as isize), r.offset(-1));
@@ -449,6 +461,7 @@ macro_rules! right {
         // Move its remaining out-of-order elements to the far left.
         debug_assert_eq!(width(l, r), block_r);
         while start_r < end_r {
+            // SAFETY: See the reasoning in [remaining-elements-safety].
             unsafe {
                 end_r = end_r.offset(-1);
                 ptr::swap(l, r.offset(-(*end_r as isize) - 1));
@@ -481,6 +494,8 @@ fn partition<T, F>(v: &mut [T], pivot: usize, is_less: &mut F) -> (usize, bool)
 
         // Read the pivot into a stack-allocated variable for efficiency. If a following comparison
         // operation panics, the pivot will be automatically written back into the slice.
+
+        // SAFETY: `pivot` is a reference to the first element of `v`, so `ptr::read` is safe.
         let mut tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) });
         let _pivot_guard = CopyOnDrop { src: &mut *tmp, dest: pivot };
         let pivot = &*tmp;
@@ -646,6 +661,12 @@ fn choose_pivot<T, F>(v: &mut [T], is_less: &mut F) -> (usize, bool)
 
     if len >= 8 {
         // Swaps indices so that `v[a] <= v[b]`.
+        // SAFETY: `len >= 8` so there are at least two elements in the neighborhoods of
+        // `a`, `b` and `c`. This means the three calls to `sort_adjacent` result in
+        // corresponding calls to `sort3` with valid 3-item neighborhoods around each
+        // pointer, which in turn means the calls to `sort2` are done with valid
+        // references. Thus the `v.get_unchecked` calls are safe, as is the `ptr::swap`
+        // call.
         let mut sort2 = |a: &mut usize, b: &mut usize| unsafe {
             if is_less(v.get_unchecked(*b), v.get_unchecked(*a)) {
                 ptr::swap(a, b);
diff --git a/library/core/tests/convert.rs b/library/core/tests/convert.rs
new file mode 100644 (file)
index 0000000..f1048f4
--- /dev/null
@@ -0,0 +1,16 @@
+#[test]
+fn convert() {
+    const fn from(x: i32) -> i32 {
+        i32::from(x)
+    }
+
+    const FOO: i32 = from(42);
+    assert_eq!(FOO, 42);
+
+    const fn into(x: Vec<String>) -> Vec<String> {
+        x.into()
+    }
+
+    const BAR: Vec<String> = into(Vec::new());
+    assert_eq!(BAR, Vec::<String>::new());
+}
index cd3aed4cd28f81a7d90c9f7fc15cfb2a99d97d19..f25106abc88212ece54032050cbc801347c594b9 100644 (file)
@@ -9,12 +9,13 @@
 #![feature(cfg_target_has_atomic)]
 #![feature(const_assume)]
 #![feature(const_cell_into_inner)]
+#![feature(const_convert)]
 #![feature(const_maybe_uninit_assume_init)]
+#![feature(const_num_from_num)]
 #![feature(const_ptr_read)]
 #![feature(const_ptr_write)]
 #![feature(const_ptr_offset)]
 #![feature(const_trait_impl)]
-#![feature(const_num_from_num)]
 #![feature(core_intrinsics)]
 #![feature(core_private_bignum)]
 #![feature(core_private_diy_float)]
@@ -83,6 +84,7 @@
 mod clone;
 mod cmp;
 mod const_ptr;
+mod convert;
 mod fmt;
 mod hash;
 mod intrinsics;
index 4580f9a7758f33351da3a1ca24706121c13982e1..ac75ce7f2211046ac7a17f3d25b170a9dcbb5b46 100644 (file)
@@ -44,6 +44,7 @@ unsafe fn abort() -> ! {
                 libc::abort();
             }
         } else if #[cfg(any(target_os = "hermit",
+                            target_os = "solid_asp3",
                             all(target_vendor = "fortanix", target_env = "sgx")
         ))] {
             unsafe fn abort() -> ! {
index ac7d8c18e3e029282473c1f695ea1ef1d993eb2c..b5d0ca2572c93adc9841a72943b788d67e190b5e 100644 (file)
@@ -45,6 +45,7 @@
     } else if #[cfg(any(
         all(target_family = "windows", target_env = "gnu"),
         target_os = "psp",
+        target_os = "solid_asp3",
         all(target_family = "unix", not(target_os = "espidf")),
         all(target_vendor = "fortanix", target_env = "sgx"),
     ))] {
index 726157c1f1a41a9e1dd301264f825dfaeadba2c4..cc7184d57f178a26a94f1139d72c21cad54c6015 100644 (file)
@@ -27,6 +27,7 @@ fn main() {
         || target.contains("wasm32")
         || target.contains("asmjs")
         || target.contains("espidf")
+        || target.contains("solid")
     {
         // These platforms don't have any special requirements.
     } else {
index ba084987f66e1d532f33767ad2b00d6f112c15a0..fe1f28f00c4031aa8291120b6afc9ba5b842ffef 100644 (file)
@@ -409,6 +409,8 @@ fn _new(bytes: Vec<u8>) -> Result<CString, NulError> {
     /// Creates a C-compatible string by consuming a byte vector,
     /// without checking for interior 0 bytes.
     ///
+    /// Trailing 0 byte will be appended by this function.
+    ///
     /// This method is equivalent to [`CString::new`] except that no runtime
     /// assertion is made that `v` contains no 0 bytes, and it requires an
     /// actual byte vector, not anything that can be converted to one with Into.
index f69baba9e733f748d140972e08d0e2269904f2b7..b33a3c5d22fe1de207701ad1727b5ff2f49e83af 100644 (file)
 #![feature(maybe_uninit_slice)]
 #![feature(maybe_uninit_uninit_array)]
 #![feature(min_specialization)]
+#![cfg_attr(not(bootstrap), feature(must_not_suspend))]
 #![feature(needs_panic_runtime)]
 #![feature(negative_impls)]
 #![feature(never_type)]
@@ -520,20 +521,20 @@ pub mod task {
     pub use alloc::task::*;
 }
 
-// Platform-abstraction modules
+// The runtime entry point and a few unstable public functions used by the
+// compiler
 #[macro_use]
-mod sys_common;
+pub mod rt;
+
+// Platform-abstraction modules
 mod sys;
+mod sys_common;
 
 pub mod alloc;
 
 // Private support modules
 mod panicking;
 
-// The runtime entry point and a few unstable public functions used by the
-// compiler
-pub mod rt;
-
 #[path = "../../backtrace/src/lib.rs"]
 #[allow(dead_code, unused_attributes)]
 mod backtrace_rs;
index 069a5376a441c9062b8e945ba0f7610b46ddcbc4..90c30313dbbda7bc1e94e2d97ac2ecd7af5b1884 100644 (file)
@@ -138,6 +138,8 @@ pub mod windows {}
 #[cfg(target_os = "solaris")]
 pub mod solaris;
 
+#[cfg(target_os = "solid_asp3")]
+pub mod solid;
 #[cfg(target_os = "vxworks")]
 pub mod vxworks;
 
diff --git a/library/std/src/os/solid/ffi.rs b/library/std/src/os/solid/ffi.rs
new file mode 100644 (file)
index 0000000..aaa2070
--- /dev/null
@@ -0,0 +1,41 @@
+//! SOLID-specific extension to the primitives in the `std::ffi` module
+//!
+//! # Examples
+//!
+//! ```
+//! use std::ffi::OsString;
+//! use std::os::solid::ffi::OsStringExt;
+//!
+//! let bytes = b"foo".to_vec();
+//!
+//! // OsStringExt::from_vec
+//! let os_string = OsString::from_vec(bytes);
+//! assert_eq!(os_string.to_str(), Some("foo"));
+//!
+//! // OsStringExt::into_vec
+//! let bytes = os_string.into_vec();
+//! assert_eq!(bytes, b"foo");
+//! ```
+//!
+//! ```
+//! use std::ffi::OsStr;
+//! use std::os::solid::ffi::OsStrExt;
+//!
+//! let bytes = b"foo";
+//!
+//! // OsStrExt::from_bytes
+//! let os_str = OsStr::from_bytes(bytes);
+//! assert_eq!(os_str.to_str(), Some("foo"));
+//!
+//! // OsStrExt::as_bytes
+//! let bytes = os_str.as_bytes();
+//! assert_eq!(bytes, b"foo");
+//! ```
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+#[path = "../unix/ffi/os_str.rs"]
+mod os_str;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use self::os_str::{OsStrExt, OsStringExt};
diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs
new file mode 100644 (file)
index 0000000..33cc5a0
--- /dev/null
@@ -0,0 +1,113 @@
+//! SOLID-specific extensions to general I/O primitives
+
+#![deny(unsafe_op_in_unsafe_fn)]
+#![unstable(feature = "solid_ext", issue = "none")]
+
+use crate::net;
+use crate::sys;
+use crate::sys_common::{self, AsInner, FromInner, IntoInner};
+
+/// Raw file descriptors.
+pub type RawFd = i32;
+
+/// A trait to extract the raw SOLID Sockets file descriptor from an underlying
+/// object.
+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 guaranteed to be valid while
+    /// the original object has not yet been destroyed.
+    fn as_raw_fd(&self) -> RawFd;
+}
+
+/// A trait to express the ability to construct an object from a raw file
+/// descriptor.
+pub trait FromRawFd {
+    /// Constructs a new instance of `Self` from the given raw file
+    /// descriptor.
+    ///
+    /// This function **consumes ownership** of the specified file
+    /// descriptor. The returned object will take responsibility for closing
+    /// it when the object goes out of scope.
+    ///
+    /// This function is also unsafe as the primitives currently returned
+    /// have the contract that they are the sole owner of the file
+    /// descriptor they are wrapping. Usage of this function could
+    /// accidentally allow violating this contract which can cause memory
+    /// unsafety in code that relies on it being true.
+    unsafe fn from_raw_fd(fd: RawFd) -> Self;
+}
+
+/// A trait to express the ability to consume an object and acquire ownership of
+/// its raw file descriptor.
+pub trait IntoRawFd {
+    /// Consumes this object, returning the raw underlying file descriptor.
+    ///
+    /// This function **transfers ownership** of the underlying file descriptor
+    /// to the caller. Callers are then the unique owners of the file descriptor
+    /// and must close the descriptor once it's no longer needed.
+    fn into_raw_fd(self) -> RawFd;
+}
+
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl AsRawFd for RawFd {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        *self
+    }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl IntoRawFd for RawFd {
+    #[inline]
+    fn into_raw_fd(self) -> RawFd {
+        self
+    }
+}
+#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")]
+impl FromRawFd for RawFd {
+    #[inline]
+    unsafe fn from_raw_fd(fd: RawFd) -> RawFd {
+        fd
+    }
+}
+
+macro_rules! impl_as_raw_fd {
+    ($($t:ident)*) => {$(
+        #[stable(feature = "rust1", since = "1.0.0")]
+        impl AsRawFd for net::$t {
+            #[inline]
+            fn as_raw_fd(&self) -> RawFd {
+                *self.as_inner().socket().as_inner()
+            }
+        }
+    )*};
+}
+impl_as_raw_fd! { TcpStream TcpListener UdpSocket }
+
+macro_rules! impl_from_raw_fd {
+    ($($t:ident)*) => {$(
+        #[stable(feature = "from_raw_os", since = "1.1.0")]
+        impl FromRawFd for net::$t {
+            #[inline]
+            unsafe fn from_raw_fd(fd: RawFd) -> net::$t {
+                let socket = sys::net::Socket::from_inner(fd);
+                net::$t::from_inner(sys_common::net::$t::from_inner(socket))
+            }
+        }
+    )*};
+}
+impl_from_raw_fd! { TcpStream TcpListener UdpSocket }
+
+macro_rules! impl_into_raw_fd {
+    ($($t:ident)*) => {$(
+        #[stable(feature = "into_raw_os", since = "1.4.0")]
+        impl IntoRawFd for net::$t {
+            #[inline]
+            fn into_raw_fd(self) -> RawFd {
+                self.into_inner().into_socket().into_inner()
+            }
+        }
+    )*};
+}
+impl_into_raw_fd! { TcpStream TcpListener UdpSocket }
diff --git a/library/std/src/os/solid/mod.rs b/library/std/src/os/solid/mod.rs
new file mode 100644 (file)
index 0000000..4328ba7
--- /dev/null
@@ -0,0 +1,17 @@
+#![stable(feature = "rust1", since = "1.0.0")]
+
+pub mod ffi;
+pub mod io;
+
+/// A prelude for conveniently writing platform-specific code.
+///
+/// Includes all extension traits, and some important type definitions.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod prelude {
+    #[doc(no_inline)]
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub use super::ffi::{OsStrExt, OsStringExt};
+    #[doc(no_inline)]
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+}
index c9b21fcf9c6d2eef7a2a006e6e3a65082d634f51..5c68400114d49744de3e73b1663db7555664b021 100644 (file)
@@ -1907,7 +1907,7 @@ pub fn wait_with_output(mut self) -> io::Result<Output> {
 /// [platform-specific behavior]: #platform-specific-behavior
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn exit(code: i32) -> ! {
-    crate::sys_common::rt::cleanup();
+    crate::rt::cleanup();
     crate::sys::os::exit(code)
 }
 
index 893167e373015ca84c7a7ec242be6c85878991aa..4d72aff011684c8c2efe874ce0e1105e405ef4e0 100644 (file)
     issue = "none"
 )]
 #![doc(hidden)]
+#![deny(unsafe_op_in_unsafe_fn)]
+#![allow(unused_macros)]
+
+use crate::ffi::CString;
 
 // Re-export some of our utilities which are expected by other crates.
 pub use crate::panicking::{begin_panic, begin_panic_fmt, panic_count};
 pub use core::panicking::panic_display;
 
+use crate::sync::Once;
+use crate::sys;
+use crate::sys_common::thread_info;
+use crate::thread::Thread;
+
+// Prints to the "panic output", depending on the platform this may be:
+// - the standard error output
+// - some dedicated platform specific output
+// - nothing (so this macro is a no-op)
+macro_rules! rtprintpanic {
+    ($($t:tt)*) => {
+        if let Some(mut out) = crate::sys::stdio::panic_output() {
+            let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*));
+        }
+    }
+}
+
+macro_rules! rtabort {
+    ($($t:tt)*) => {
+        {
+            rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*));
+            crate::sys::abort_internal();
+        }
+    }
+}
+
+macro_rules! rtassert {
+    ($e:expr) => {
+        if !$e {
+            rtabort!(concat!("assertion failed: ", stringify!($e)));
+        }
+    };
+}
+
+macro_rules! rtunwrap {
+    ($ok:ident, $e:expr) => {
+        match $e {
+            $ok(v) => v,
+            ref err => {
+                let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
+                rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
+            }
+        }
+    };
+}
+
+// One-time runtime initialization.
+// Runs before `main`.
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+#[cfg_attr(test, allow(dead_code))]
+unsafe fn init(argc: isize, argv: *const *const u8) {
+    unsafe {
+        sys::init(argc, argv);
+
+        let main_guard = sys::thread::guard::init();
+        // Next, set up the current Thread with the guard information we just
+        // created. Note that this isn't necessary in general for new threads,
+        // but we just do this to name the main thread and to give it correct
+        // info about the stack bounds.
+        let thread = Thread::new(Some(rtunwrap!(Ok, CString::new("main"))));
+        thread_info::set(main_guard, thread);
+    }
+}
+
+// One-time runtime cleanup.
+// Runs after `main` or at program exit.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub(crate) fn cleanup() {
+    static CLEANUP: Once = Once::new();
+    CLEANUP.call_once(|| unsafe {
+        // Flush stdout and disable buffering.
+        crate::io::cleanup();
+        // SAFETY: Only called once during runtime cleanup.
+        sys::cleanup();
+    });
+}
+
 // To reduce the generated code of the new `lang_start`, this function is doing
 // the real work.
 #[cfg(not(test))]
@@ -26,7 +108,7 @@ fn lang_start_internal(
     argc: isize,
     argv: *const *const u8,
 ) -> Result<isize, !> {
-    use crate::{mem, panic, sys, sys_common};
+    use crate::{mem, panic};
     let rt_abort = move |e| {
         mem::forget(e);
         rtabort!("initialization or cleanup bug");
@@ -42,14 +124,14 @@ fn lang_start_internal(
     // prevent libstd from accidentally introducing a panic to these functions. Another is from
     // user code from `main` or, more nefariously, as described in e.g. issue #86030.
     // SAFETY: Only called once during runtime initialization.
-    panic::catch_unwind(move || unsafe { sys_common::rt::init(argc, argv) }).map_err(rt_abort)?;
+    panic::catch_unwind(move || unsafe { init(argc, argv) }).map_err(rt_abort)?;
     let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
         .map_err(move |e| {
             mem::forget(e);
             rtprintpanic!("drop of the panic payload panicked");
             sys::abort_internal()
         });
-    panic::catch_unwind(sys_common::rt::cleanup).map_err(rt_abort)?;
+    panic::catch_unwind(cleanup).map_err(rt_abort)?;
     ret_code
 }
 
index e1d6324c17e336e021714b71f5ae6bd34cfb7784..06a97fd3f761022a5c5c41e8e4f9aabf0d1bfc48 100644 (file)
@@ -188,6 +188,12 @@ unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
 /// [`lock`]: Mutex::lock
 /// [`try_lock`]: Mutex::try_lock
 #[must_use = "if unused the Mutex will immediately unlock"]
+#[cfg_attr(
+    not(bootstrap),
+    must_not_suspend = "Holding a MutexGuard across suspend \
+                      points can cause deadlocks, delays, \
+                      and cause Futures to not implement `Send`"
+)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct MutexGuard<'a, T: ?Sized + 'a> {
     lock: &'a Mutex<T>,
index e50d62d8173766fa8fa7a8551c34ccde4f7ec70b..aa1ce82d96799b9a99d2ae6c44e2bde8bcad08ba 100644 (file)
@@ -95,6 +95,12 @@ unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
 /// [`read`]: RwLock::read
 /// [`try_read`]: RwLock::try_read
 #[must_use = "if unused the RwLock will immediately unlock"]
+#[cfg_attr(
+    not(bootstrap),
+    must_not_suspend = "Holding a RwLockReadGuard across suspend \
+                      points can cause deadlocks, delays, \
+                      and cause Futures to not implement `Send`"
+)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
     lock: &'a RwLock<T>,
@@ -115,6 +121,12 @@ unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {}
 /// [`write`]: RwLock::write
 /// [`try_write`]: RwLock::try_write
 #[must_use = "if unused the RwLock will immediately unlock"]
+#[cfg_attr(
+    not(bootstrap),
+    must_not_suspend = "Holding a RwLockWriteGuard across suspend \
+                      points can cause deadlocks, delays, \
+                      and cause Future's to not implement `Send`"
+)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> {
     lock: &'a RwLock<T>,
diff --git a/library/std/src/sys/itron/abi.rs b/library/std/src/sys/itron/abi.rs
new file mode 100644 (file)
index 0000000..f99ee4f
--- /dev/null
@@ -0,0 +1,155 @@
+//! ABI for Î¼ITRON derivatives
+pub type int_t = crate::os::raw::c_int;
+pub type uint_t = crate::os::raw::c_uint;
+pub type bool_t = int_t;
+
+/// Kernel object ID
+pub type ID = int_t;
+
+/// The current task.
+pub const TSK_SELF: ID = 0;
+
+/// Relative time
+pub type RELTIM = u32;
+
+/// Timeout (a valid `RELTIM` value or `TMO_FEVR`)
+pub type TMO = u32;
+
+/// The infinite timeout value
+pub const TMO_FEVR: TMO = TMO::MAX;
+
+/// The maximum valid value of `RELTIM`
+pub const TMAX_RELTIM: RELTIM = 4_000_000_000;
+
+/// System time
+pub type SYSTIM = u64;
+
+/// Error code type
+pub type ER = int_t;
+
+/// Error code type, `ID` on success
+pub type ER_ID = int_t;
+
+/// Task or interrupt priority
+pub type PRI = int_t;
+
+/// The special value of `PRI` representing the current task's priority.
+pub const TPRI_SELF: PRI = 0;
+
+/// Object attributes
+pub type ATR = uint_t;
+
+/// Use the priority inheritance protocol
+#[cfg(target_os = "solid_asp3")]
+pub const TA_INHERIT: ATR = 0x02;
+
+/// Activate the task on creation
+pub const TA_ACT: ATR = 0x01;
+
+/// The maximum count of a semaphore
+pub const TMAX_MAXSEM: uint_t = uint_t::MAX;
+
+/// Callback parameter
+pub type EXINF = isize;
+
+/// Task entrypoint
+pub type TASK = Option<unsafe extern "C" fn(EXINF)>;
+
+// Error codes
+pub const E_OK: ER = 0;
+pub const E_SYS: ER = -5;
+pub const E_NOSPT: ER = -9;
+pub const E_RSFN: ER = -10;
+pub const E_RSATR: ER = -11;
+pub const E_PAR: ER = -17;
+pub const E_ID: ER = -18;
+pub const E_CTX: ER = -25;
+pub const E_MACV: ER = -26;
+pub const E_OACV: ER = -27;
+pub const E_ILUSE: ER = -28;
+pub const E_NOMEM: ER = -33;
+pub const E_NOID: ER = -34;
+pub const E_NORES: ER = -35;
+pub const E_OBJ: ER = -41;
+pub const E_NOEXS: ER = -42;
+pub const E_QOVR: ER = -43;
+pub const E_RLWAI: ER = -49;
+pub const E_TMOUT: ER = -50;
+pub const E_DLT: ER = -51;
+pub const E_CLS: ER = -52;
+pub const E_RASTER: ER = -53;
+pub const E_WBLK: ER = -57;
+pub const E_BOVR: ER = -58;
+pub const E_COMM: ER = -65;
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct T_CSEM {
+    pub sematr: ATR,
+    pub isemcnt: uint_t,
+    pub maxsem: uint_t,
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct T_CMTX {
+    pub mtxatr: ATR,
+    pub ceilpri: PRI,
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct T_CTSK {
+    pub tskatr: ATR,
+    pub exinf: EXINF,
+    pub task: TASK,
+    pub itskpri: PRI,
+    pub stksz: usize,
+    pub stk: *mut u8,
+}
+
+extern "C" {
+    #[link_name = "__asp3_acre_tsk"]
+    pub fn acre_tsk(pk_ctsk: *const T_CTSK) -> ER_ID;
+    #[link_name = "__asp3_get_tid"]
+    pub fn get_tid(p_tskid: *mut ID) -> ER;
+    #[link_name = "__asp3_dly_tsk"]
+    pub fn dly_tsk(dlytim: RELTIM) -> ER;
+    #[link_name = "__asp3_ter_tsk"]
+    pub fn ter_tsk(tskid: ID) -> ER;
+    #[link_name = "__asp3_del_tsk"]
+    pub fn del_tsk(tskid: ID) -> ER;
+    #[link_name = "__asp3_get_pri"]
+    pub fn get_pri(tskid: ID, p_tskpri: *mut PRI) -> ER;
+    #[link_name = "__asp3_rot_rdq"]
+    pub fn rot_rdq(tskpri: PRI) -> ER;
+    #[link_name = "__asp3_slp_tsk"]
+    pub fn slp_tsk() -> ER;
+    #[link_name = "__asp3_tslp_tsk"]
+    pub fn tslp_tsk(tmout: TMO) -> ER;
+    #[link_name = "__asp3_wup_tsk"]
+    pub fn wup_tsk(tskid: ID) -> ER;
+    #[link_name = "__asp3_unl_cpu"]
+    pub fn unl_cpu() -> ER;
+    #[link_name = "__asp3_dis_dsp"]
+    pub fn dis_dsp() -> ER;
+    #[link_name = "__asp3_ena_dsp"]
+    pub fn ena_dsp() -> ER;
+    #[link_name = "__asp3_sns_dsp"]
+    pub fn sns_dsp() -> bool_t;
+    #[link_name = "__asp3_get_tim"]
+    pub fn get_tim(p_systim: *mut SYSTIM) -> ER;
+    #[link_name = "__asp3_acre_mtx"]
+    pub fn acre_mtx(pk_cmtx: *const T_CMTX) -> ER_ID;
+    #[link_name = "__asp3_del_mtx"]
+    pub fn del_mtx(tskid: ID) -> ER;
+    #[link_name = "__asp3_loc_mtx"]
+    pub fn loc_mtx(mtxid: ID) -> ER;
+    #[link_name = "__asp3_ploc_mtx"]
+    pub fn ploc_mtx(mtxid: ID) -> ER;
+    #[link_name = "__asp3_tloc_mtx"]
+    pub fn tloc_mtx(mtxid: ID, tmout: TMO) -> ER;
+    #[link_name = "__asp3_unl_mtx"]
+    pub fn unl_mtx(mtxid: ID) -> ER;
+    pub fn exd_tsk() -> ER;
+}
diff --git a/library/std/src/sys/itron/condvar.rs b/library/std/src/sys/itron/condvar.rs
new file mode 100644 (file)
index 0000000..dac4b8a
--- /dev/null
@@ -0,0 +1,294 @@
+//! POSIX conditional variable implementation based on user-space wait queues.
+use super::{abi, error::expect_success_aborting, spin::SpinMutex, task, time::with_tmos_strong};
+use crate::{mem::replace, ptr::NonNull, sys::mutex::Mutex, time::Duration};
+
+// The implementation is inspired by the queue-based implementation shown in
+// Andrew D. Birrell's paper "Implementing Condition Variables with Semaphores"
+
+pub struct Condvar {
+    waiters: SpinMutex<waiter_queue::WaiterQueue>,
+}
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+pub type MovableCondvar = Condvar;
+
+impl Condvar {
+    pub const fn new() -> Condvar {
+        Condvar { waiters: SpinMutex::new(waiter_queue::WaiterQueue::new()) }
+    }
+
+    pub unsafe fn init(&mut self) {}
+
+    pub unsafe fn notify_one(&self) {
+        self.waiters.with_locked(|waiters| {
+            if let Some(task) = waiters.pop_front() {
+                // Unpark the task
+                match unsafe { abi::wup_tsk(task) } {
+                    // The task already has a token.
+                    abi::E_QOVR => {}
+                    // Can't undo the effect; abort the program on failure
+                    er => {
+                        expect_success_aborting(er, &"wup_tsk");
+                    }
+                }
+            }
+        });
+    }
+
+    pub unsafe fn notify_all(&self) {
+        self.waiters.with_locked(|waiters| {
+            while let Some(task) = waiters.pop_front() {
+                // Unpark the task
+                match unsafe { abi::wup_tsk(task) } {
+                    // The task already has a token.
+                    abi::E_QOVR => {}
+                    // Can't undo the effect; abort the program on failure
+                    er => {
+                        expect_success_aborting(er, &"wup_tsk");
+                    }
+                }
+            }
+        });
+    }
+
+    pub unsafe fn wait(&self, mutex: &Mutex) {
+        // Construct `Waiter`.
+        let mut waiter = waiter_queue::Waiter::new();
+        let waiter = NonNull::from(&mut waiter);
+
+        self.waiters.with_locked(|waiters| unsafe {
+            waiters.insert(waiter);
+        });
+
+        unsafe { mutex.unlock() };
+
+        // Wait until `waiter` is removed from the queue
+        loop {
+            // Park the current task
+            expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
+
+            if !self.waiters.with_locked(|waiters| unsafe { waiters.is_queued(waiter) }) {
+                break;
+            }
+        }
+
+        unsafe { mutex.lock() };
+    }
+
+    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+        // Construct and pin `Waiter`
+        let mut waiter = waiter_queue::Waiter::new();
+        let waiter = NonNull::from(&mut waiter);
+
+        self.waiters.with_locked(|waiters| unsafe {
+            waiters.insert(waiter);
+        });
+
+        unsafe { mutex.unlock() };
+
+        // Park the current task and do not wake up until the timeout elapses
+        // or the task gets woken up by `notify_*`
+        match with_tmos_strong(dur, |tmo| {
+            let er = unsafe { abi::tslp_tsk(tmo) };
+            if er == 0 {
+                // We were unparked. Are we really dequeued?
+                if self.waiters.with_locked(|waiters| unsafe { waiters.is_queued(waiter) }) {
+                    // No we are not. Continue waiting.
+                    return abi::E_TMOUT;
+                }
+            }
+            er
+        }) {
+            abi::E_TMOUT => {}
+            er => {
+                expect_success_aborting(er, &"tslp_tsk");
+            }
+        }
+
+        // Remove `waiter` from `self.waiters`. If `waiter` is still in
+        // `waiters`, it means we woke up because of a timeout. Otherwise,
+        // we woke up because of `notify_*`.
+        let success = self.waiters.with_locked(|waiters| unsafe { !waiters.remove(waiter) });
+
+        unsafe { mutex.lock() };
+        success
+    }
+
+    pub unsafe fn destroy(&self) {}
+}
+
+mod waiter_queue {
+    use super::*;
+
+    pub struct WaiterQueue {
+        head: Option<ListHead>,
+    }
+
+    #[derive(Copy, Clone)]
+    struct ListHead {
+        first: NonNull<Waiter>,
+        last: NonNull<Waiter>,
+    }
+
+    unsafe impl Send for ListHead {}
+    unsafe impl Sync for ListHead {}
+
+    pub struct Waiter {
+        // These fields are only accessed through `&[mut] WaiterQueue`.
+        /// The waiting task's ID. Will be zeroed when the task is woken up
+        /// and removed from a queue.
+        task: abi::ID,
+        priority: abi::PRI,
+        prev: Option<NonNull<Waiter>>,
+        next: Option<NonNull<Waiter>>,
+    }
+
+    unsafe impl Send for Waiter {}
+    unsafe impl Sync for Waiter {}
+
+    impl Waiter {
+        #[inline]
+        pub fn new() -> Self {
+            let task = task::current_task_id();
+            let priority = task::task_priority(abi::TSK_SELF);
+
+            // Zeroness of `Waiter::task` indicates whether the `Waiter` is
+            // linked to a queue or not. This invariant is important for
+            // the correctness.
+            debug_assert_ne!(task, 0);
+
+            Self { task, priority, prev: None, next: None }
+        }
+    }
+
+    impl WaiterQueue {
+        #[inline]
+        pub const fn new() -> Self {
+            Self { head: None }
+        }
+
+        /// # Safety
+        ///
+        ///  - The caller must own `*waiter_ptr`. The caller will lose the
+        ///    ownership until `*waiter_ptr` is removed from `self`.
+        ///
+        ///  - `*waiter_ptr` must be valid until it's removed from the queue.
+        ///
+        ///  - `*waiter_ptr` must not have been previously inserted to a `WaiterQueue`.
+        ///
+        pub unsafe fn insert(&mut self, mut waiter_ptr: NonNull<Waiter>) {
+            unsafe {
+                let waiter = waiter_ptr.as_mut();
+
+                debug_assert!(waiter.prev.is_none());
+                debug_assert!(waiter.next.is_none());
+
+                if let Some(head) = &mut self.head {
+                    // Find the insertion position and insert `waiter`
+                    let insert_after = {
+                        let mut cursor = head.last;
+                        loop {
+                            if waiter.priority <= cursor.as_ref().priority {
+                                // `cursor` and all previous waiters have the same or higher
+                                // priority than `current_task_priority`. Insert the new
+                                // waiter right after `cursor`.
+                                break Some(cursor);
+                            }
+                            cursor = if let Some(prev) = cursor.as_ref().prev {
+                                prev
+                            } else {
+                                break None;
+                            };
+                        }
+                    };
+
+                    if let Some(mut insert_after) = insert_after {
+                        // Insert `waiter` after `insert_after`
+                        let insert_before = insert_after.as_ref().prev;
+
+                        waiter.prev = Some(insert_after);
+                        insert_after.as_mut().next = Some(waiter_ptr);
+
+                        waiter.next = insert_before;
+                        if let Some(mut insert_before) = insert_before {
+                            insert_before.as_mut().prev = Some(waiter_ptr);
+                        }
+                    } else {
+                        // Insert `waiter` to the front
+                        waiter.next = Some(head.first);
+                        head.first.as_mut().prev = Some(waiter_ptr);
+                        head.first = waiter_ptr;
+                    }
+                } else {
+                    // `waiter` is the only element
+                    self.head = Some(ListHead { first: waiter_ptr, last: waiter_ptr });
+                }
+            }
+        }
+
+        /// Given a `Waiter` that was previously inserted to `self`, remove
+        /// it from `self` if it's still there.
+        #[inline]
+        pub unsafe fn remove(&mut self, mut waiter_ptr: NonNull<Waiter>) -> bool {
+            unsafe {
+                let waiter = waiter_ptr.as_mut();
+                if waiter.task != 0 {
+                    let head = self.head.as_mut().unwrap();
+
+                    match (waiter.prev, waiter.next) {
+                        (Some(mut prev), Some(mut next)) => {
+                            prev.as_mut().next = Some(next);
+                            next.as_mut().next = Some(prev);
+                        }
+                        (None, Some(mut next)) => {
+                            head.first = next;
+                            next.as_mut().next = None;
+                        }
+                        (Some(mut prev), None) => {
+                            prev.as_mut().next = None;
+                            head.last = prev;
+                        }
+                        (None, None) => {
+                            self.head = None;
+                        }
+                    }
+
+                    waiter.task = 0;
+
+                    true
+                } else {
+                    false
+                }
+            }
+        }
+
+        /// Given a `Waiter` that was previously inserted to `self`, return a
+        /// flag indicating whether it's still in `self`.
+        #[inline]
+        pub unsafe fn is_queued(&self, waiter: NonNull<Waiter>) -> bool {
+            unsafe { waiter.as_ref().task != 0 }
+        }
+
+        pub fn pop_front(&mut self) -> Option<abi::ID> {
+            unsafe {
+                let head = self.head.as_mut()?;
+                let waiter = head.first.as_mut();
+
+                // Get the ID
+                let id = replace(&mut waiter.task, 0);
+
+                // Unlink the waiter
+                if let Some(mut next) = waiter.next {
+                    head.first = next;
+                    next.as_mut().prev = None;
+                } else {
+                    self.head = None;
+                }
+
+                Some(id)
+            }
+        }
+    }
+}
diff --git a/library/std/src/sys/itron/error.rs b/library/std/src/sys/itron/error.rs
new file mode 100644 (file)
index 0000000..830c60d
--- /dev/null
@@ -0,0 +1,159 @@
+use crate::{fmt, io::ErrorKind};
+
+use super::abi;
+
+/// Wraps a Î¼ITRON error code.
+#[derive(Debug, Copy, Clone)]
+pub struct ItronError {
+    er: abi::ER,
+}
+
+impl ItronError {
+    /// Construct `ItronError` from the specified error code. Returns `None` if the
+    /// error code does not represent a failure or warning.
+    #[inline]
+    pub fn new(er: abi::ER) -> Option<Self> {
+        if er < 0 { Some(Self { er }) } else { None }
+    }
+
+    /// Returns `Ok(er)` if `er` represents a success or `Err(_)` otherwise.
+    #[inline]
+    pub fn err_if_negative(er: abi::ER) -> Result<abi::ER, Self> {
+        if let Some(error) = Self::new(er) { Err(error) } else { Ok(er) }
+    }
+
+    /// Get the raw error code.
+    #[inline]
+    pub fn as_raw(&self) -> abi::ER {
+        self.er
+    }
+}
+
+impl fmt::Display for ItronError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // Allow the platforms to extend `error_name`
+        if let Some(name) = crate::sys::error::error_name(self.er) {
+            write!(f, "{} ({})", name, self.er)
+        } else {
+            write!(f, "{}", self.er)
+        }
+    }
+}
+
+/// Describe the specified Î¼ITRON error code. Returns `None` if it's an
+/// undefined error code.
+pub fn error_name(er: abi::ER) -> Option<&'static str> {
+    match er {
+        // Success
+        er if er >= 0 => None,
+
+        // Î¼ITRON 4.0
+        abi::E_SYS => Some("system error"),
+        abi::E_NOSPT => Some("unsupported function"),
+        abi::E_RSFN => Some("reserved function code"),
+        abi::E_RSATR => Some("reserved attribute"),
+        abi::E_PAR => Some("parameter error"),
+        abi::E_ID => Some("invalid ID number"),
+        abi::E_CTX => Some("context error"),
+        abi::E_MACV => Some("memory access violation"),
+        abi::E_OACV => Some("object access violation"),
+        abi::E_ILUSE => Some("illegal service call use"),
+        abi::E_NOMEM => Some("insufficient memory"),
+        abi::E_NOID => Some("no ID number available"),
+        abi::E_OBJ => Some("object state error"),
+        abi::E_NOEXS => Some("non-existent object"),
+        abi::E_QOVR => Some("queue overflow"),
+        abi::E_RLWAI => Some("forced release from waiting"),
+        abi::E_TMOUT => Some("polling failure or timeout"),
+        abi::E_DLT => Some("waiting object deleted"),
+        abi::E_CLS => Some("waiting object state changed"),
+        abi::E_WBLK => Some("non-blocking code accepted"),
+        abi::E_BOVR => Some("buffer overflow"),
+
+        // The TOPPERS third generation kernels
+        abi::E_NORES => Some("insufficient system resources"),
+        abi::E_RASTER => Some("termination request raised"),
+        abi::E_COMM => Some("communication failure"),
+
+        _ => None,
+    }
+}
+
+pub fn decode_error_kind(er: abi::ER) -> ErrorKind {
+    match er {
+        // Success
+        er if er >= 0 => ErrorKind::Uncategorized,
+
+        // Î¼ITRON 4.0
+        // abi::E_SYS
+        abi::E_NOSPT => ErrorKind::Unsupported, // Some("unsupported function"),
+        abi::E_RSFN => ErrorKind::InvalidInput, // Some("reserved function code"),
+        abi::E_RSATR => ErrorKind::InvalidInput, // Some("reserved attribute"),
+        abi::E_PAR => ErrorKind::InvalidInput,  // Some("parameter error"),
+        abi::E_ID => ErrorKind::NotFound,       // Some("invalid ID number"),
+        // abi::E_CTX
+        abi::E_MACV => ErrorKind::PermissionDenied, // Some("memory access violation"),
+        abi::E_OACV => ErrorKind::PermissionDenied, // Some("object access violation"),
+        // abi::E_ILUSE
+        abi::E_NOMEM => ErrorKind::OutOfMemory, // Some("insufficient memory"),
+        abi::E_NOID => ErrorKind::OutOfMemory,  // Some("no ID number available"),
+        // abi::E_OBJ
+        abi::E_NOEXS => ErrorKind::NotFound, // Some("non-existent object"),
+        // abi::E_QOVR
+        abi::E_RLWAI => ErrorKind::Interrupted, // Some("forced release from waiting"),
+        abi::E_TMOUT => ErrorKind::TimedOut,    // Some("polling failure or timeout"),
+        // abi::E_DLT
+        // abi::E_CLS
+        // abi::E_WBLK
+        // abi::E_BOVR
+
+        // The TOPPERS third generation kernels
+        abi::E_NORES => ErrorKind::OutOfMemory, // Some("insufficient system resources"),
+        // abi::E_RASTER
+        // abi::E_COMM
+        _ => ErrorKind::Uncategorized,
+    }
+}
+
+/// Similar to `ItronError::err_if_negative(er).expect()` except that, while
+/// panicking, it prints the message to `panic_output` and aborts the program
+/// instead. This ensures the error message is not obscured by double
+/// panicking.
+///
+/// This is useful for diagnosing creation failures of synchronization
+/// primitives that are used by `std`'s internal mechanisms. Such failures
+/// are common when the system is mis-configured to provide a too-small pool for
+/// kernel objects.
+#[inline]
+pub fn expect_success(er: abi::ER, msg: &&str) -> abi::ER {
+    match ItronError::err_if_negative(er) {
+        Ok(x) => x,
+        Err(e) => fail(e, msg),
+    }
+}
+
+/// Similar to `ItronError::err_if_negative(er).expect()` but aborts instead.
+///
+/// Use this where panicking is not allowed or the effect of the failure
+/// would be persistent.
+#[inline]
+pub fn expect_success_aborting(er: abi::ER, msg: &&str) -> abi::ER {
+    match ItronError::err_if_negative(er) {
+        Ok(x) => x,
+        Err(e) => fail_aborting(e, msg),
+    }
+}
+
+#[cold]
+pub fn fail(e: impl fmt::Display, msg: &&str) -> ! {
+    if crate::thread::panicking() {
+        fail_aborting(e, msg)
+    } else {
+        panic!("{} failed: {}", *msg, e)
+    }
+}
+
+#[cold]
+pub fn fail_aborting(e: impl fmt::Display, msg: &&str) -> ! {
+    rtabort!("{} failed: {}", *msg, e)
+}
diff --git a/library/std/src/sys/itron/mutex.rs b/library/std/src/sys/itron/mutex.rs
new file mode 100644 (file)
index 0000000..e01f595
--- /dev/null
@@ -0,0 +1,183 @@
+//! Mutex implementation backed by Î¼ITRON mutexes. Assumes `acre_mtx` and
+//! `TA_INHERIT` are available.
+use super::{
+    abi,
+    error::{expect_success, expect_success_aborting, fail, ItronError},
+    spin::SpinIdOnceCell,
+};
+use crate::cell::UnsafeCell;
+
+pub struct Mutex {
+    /// The ID of the underlying mutex object
+    mtx: SpinIdOnceCell<()>,
+}
+
+pub type MovableMutex = Mutex;
+
+/// Create a mutex object. This function never panics.
+fn new_mtx() -> Result<abi::ID, ItronError> {
+    ItronError::err_if_negative(unsafe {
+        abi::acre_mtx(&abi::T_CMTX {
+            // Priority inheritance mutex
+            mtxatr: abi::TA_INHERIT,
+            // Unused
+            ceilpri: 0,
+        })
+    })
+}
+
+impl Mutex {
+    pub const fn new() -> Mutex {
+        Mutex { mtx: SpinIdOnceCell::new() }
+    }
+
+    pub unsafe fn init(&mut self) {
+        // Initialize `self.mtx` eagerly
+        let id = new_mtx().unwrap_or_else(|e| fail(e, &"acre_mtx"));
+        unsafe { self.mtx.set_unchecked((id, ())) };
+    }
+
+    /// Get the inner mutex's ID, which is lazily created.
+    fn raw(&self) -> abi::ID {
+        match self.mtx.get_or_try_init(|| new_mtx().map(|id| (id, ()))) {
+            Ok((id, ())) => id,
+            Err(e) => fail(e, &"acre_mtx"),
+        }
+    }
+
+    pub unsafe fn lock(&self) {
+        let mtx = self.raw();
+        expect_success(unsafe { abi::loc_mtx(mtx) }, &"loc_mtx");
+    }
+
+    pub unsafe fn unlock(&self) {
+        let mtx = unsafe { self.mtx.get_unchecked().0 };
+        expect_success_aborting(unsafe { abi::unl_mtx(mtx) }, &"unl_mtx");
+    }
+
+    pub unsafe fn try_lock(&self) -> bool {
+        let mtx = self.raw();
+        match unsafe { abi::ploc_mtx(mtx) } {
+            abi::E_TMOUT => false,
+            er => {
+                expect_success(er, &"ploc_mtx");
+                true
+            }
+        }
+    }
+
+    pub unsafe fn destroy(&self) {
+        if let Some(mtx) = self.mtx.get().map(|x| x.0) {
+            expect_success_aborting(unsafe { abi::del_mtx(mtx) }, &"del_mtx");
+        }
+    }
+}
+
+pub(super) struct MutexGuard<'a>(&'a Mutex);
+
+impl<'a> MutexGuard<'a> {
+    #[inline]
+    pub(super) fn lock(x: &'a Mutex) -> Self {
+        unsafe { x.lock() };
+        Self(x)
+    }
+}
+
+impl Drop for MutexGuard<'_> {
+    #[inline]
+    fn drop(&mut self) {
+        unsafe { self.0.unlock() };
+    }
+}
+
+// All empty stubs because this platform does not yet support threads, so lock
+// acquisition always succeeds.
+pub struct ReentrantMutex {
+    /// The ID of the underlying mutex object
+    mtx: abi::ID,
+    /// The lock count.
+    count: UnsafeCell<usize>,
+}
+
+unsafe impl Send for ReentrantMutex {}
+unsafe impl Sync for ReentrantMutex {}
+
+impl ReentrantMutex {
+    pub const unsafe fn uninitialized() -> ReentrantMutex {
+        ReentrantMutex { mtx: 0, count: UnsafeCell::new(0) }
+    }
+
+    pub unsafe fn init(&mut self) {
+        self.mtx = expect_success(
+            unsafe {
+                abi::acre_mtx(&abi::T_CMTX {
+                    // Priority inheritance mutex
+                    mtxatr: abi::TA_INHERIT,
+                    // Unused
+                    ceilpri: 0,
+                })
+            },
+            &"acre_mtx",
+        );
+    }
+
+    pub unsafe fn lock(&self) {
+        match unsafe { abi::loc_mtx(self.mtx) } {
+            abi::E_OBJ => {
+                // Recursive lock
+                unsafe {
+                    let count = &mut *self.count.get();
+                    if let Some(new_count) = count.checked_add(1) {
+                        *count = new_count;
+                    } else {
+                        // counter overflow
+                        rtabort!("lock count overflow");
+                    }
+                }
+            }
+            er => {
+                expect_success(er, &"loc_mtx");
+            }
+        }
+    }
+
+    pub unsafe fn unlock(&self) {
+        unsafe {
+            let count = &mut *self.count.get();
+            if *count > 0 {
+                *count -= 1;
+                return;
+            }
+        }
+
+        expect_success_aborting(unsafe { abi::unl_mtx(self.mtx) }, &"unl_mtx");
+    }
+
+    pub unsafe fn try_lock(&self) -> bool {
+        let er = unsafe { abi::ploc_mtx(self.mtx) };
+        if er == abi::E_OBJ {
+            // Recursive lock
+            unsafe {
+                let count = &mut *self.count.get();
+                if let Some(new_count) = count.checked_add(1) {
+                    *count = new_count;
+                } else {
+                    // counter overflow
+                    rtabort!("lock count overflow");
+                }
+            }
+            true
+        } else if er == abi::E_TMOUT {
+            // Locked by another thread
+            false
+        } else {
+            expect_success(er, &"ploc_mtx");
+            // Top-level lock by the current thread
+            true
+        }
+    }
+
+    pub unsafe fn destroy(&self) {
+        expect_success_aborting(unsafe { abi::del_mtx(self.mtx) }, &"del_mtx");
+    }
+}
diff --git a/library/std/src/sys/itron/spin.rs b/library/std/src/sys/itron/spin.rs
new file mode 100644 (file)
index 0000000..d0149d1
--- /dev/null
@@ -0,0 +1,164 @@
+use super::abi;
+use crate::{
+    cell::UnsafeCell,
+    convert::TryFrom,
+    mem::MaybeUninit,
+    sync::atomic::{AtomicBool, AtomicUsize, Ordering},
+};
+
+/// A mutex implemented by `dis_dsp` (for intra-core synchronization) and a
+/// spinlock (for inter-core synchronization).
+pub struct SpinMutex<T = ()> {
+    locked: AtomicBool,
+    data: UnsafeCell<T>,
+}
+
+impl<T> SpinMutex<T> {
+    #[inline]
+    pub const fn new(x: T) -> Self {
+        Self { locked: AtomicBool::new(false), data: UnsafeCell::new(x) }
+    }
+
+    /// Acquire a lock.
+    #[inline]
+    pub fn with_locked<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
+        struct SpinMutexGuard<'a>(&'a AtomicBool);
+
+        impl Drop for SpinMutexGuard<'_> {
+            #[inline]
+            fn drop(&mut self) {
+                self.0.store(false, Ordering::Release);
+                unsafe { abi::ena_dsp() };
+            }
+        }
+
+        let _guard;
+        if unsafe { abi::sns_dsp() } == 0 {
+            let er = unsafe { abi::dis_dsp() };
+            debug_assert!(er >= 0);
+
+            // Wait until the current processor acquires a lock.
+            while self.locked.swap(true, Ordering::Acquire) {}
+
+            _guard = SpinMutexGuard(&self.locked);
+        }
+
+        f(unsafe { &mut *self.data.get() })
+    }
+}
+
+/// `OnceCell<(abi::ID, T)>` implemented by `dis_dsp` (for intra-core
+/// synchronization) and a spinlock (for inter-core synchronization).
+///
+/// It's assumed that `0` is not a valid ID, and all kernel
+/// object IDs fall into range `1..=usize::MAX`.
+pub struct SpinIdOnceCell<T = ()> {
+    id: AtomicUsize,
+    spin: SpinMutex<()>,
+    extra: UnsafeCell<MaybeUninit<T>>,
+}
+
+const ID_UNINIT: usize = 0;
+
+impl<T> SpinIdOnceCell<T> {
+    #[inline]
+    pub const fn new() -> Self {
+        Self {
+            id: AtomicUsize::new(ID_UNINIT),
+            extra: UnsafeCell::new(MaybeUninit::uninit()),
+            spin: SpinMutex::new(()),
+        }
+    }
+
+    #[inline]
+    pub fn get(&self) -> Option<(abi::ID, &T)> {
+        match self.id.load(Ordering::Acquire) {
+            ID_UNINIT => None,
+            id => Some((id as abi::ID, unsafe { (&*self.extra.get()).assume_init_ref() })),
+        }
+    }
+
+    #[inline]
+    pub fn get_mut(&mut self) -> Option<(abi::ID, &mut T)> {
+        match *self.id.get_mut() {
+            ID_UNINIT => None,
+            id => Some((id as abi::ID, unsafe { (&mut *self.extra.get()).assume_init_mut() })),
+        }
+    }
+
+    #[inline]
+    pub unsafe fn get_unchecked(&self) -> (abi::ID, &T) {
+        (self.id.load(Ordering::Acquire) as abi::ID, unsafe {
+            (&*self.extra.get()).assume_init_ref()
+        })
+    }
+
+    /// Assign the content without checking if it's already initialized or
+    /// being initialized.
+    pub unsafe fn set_unchecked(&self, (id, extra): (abi::ID, T)) {
+        debug_assert!(self.get().is_none());
+
+        // Assumption: A positive `abi::ID` fits in `usize`.
+        debug_assert!(id >= 0);
+        debug_assert!(usize::try_from(id).is_ok());
+        let id = id as usize;
+
+        unsafe { *self.extra.get() = MaybeUninit::new(extra) };
+        self.id.store(id, Ordering::Release);
+    }
+
+    /// Gets the contents of the cell, initializing it with `f` if
+    /// the cell was empty. If the cell was empty and `f` failed, an
+    /// error is returned.
+    ///
+    /// Warning: `f` must not perform a blocking operation, which
+    /// includes panicking.
+    #[inline]
+    pub fn get_or_try_init<F, E>(&self, f: F) -> Result<(abi::ID, &T), E>
+    where
+        F: FnOnce() -> Result<(abi::ID, T), E>,
+    {
+        // Fast path
+        if let Some(x) = self.get() {
+            return Ok(x);
+        }
+
+        self.initialize(f)?;
+
+        debug_assert!(self.get().is_some());
+
+        // Safety: The inner value has been initialized
+        Ok(unsafe { self.get_unchecked() })
+    }
+
+    fn initialize<F, E>(&self, f: F) -> Result<(), E>
+    where
+        F: FnOnce() -> Result<(abi::ID, T), E>,
+    {
+        self.spin.with_locked(|_| {
+            if self.id.load(Ordering::Relaxed) == ID_UNINIT {
+                let (initialized_id, initialized_extra) = f()?;
+
+                // Assumption: A positive `abi::ID` fits in `usize`.
+                debug_assert!(initialized_id >= 0);
+                debug_assert!(usize::try_from(initialized_id).is_ok());
+                let initialized_id = initialized_id as usize;
+
+                // Store the initialized contents. Use the release ordering to
+                // make sure the write is visible to the callers of `get`.
+                unsafe { *self.extra.get() = MaybeUninit::new(initialized_extra) };
+                self.id.store(initialized_id, Ordering::Release);
+            }
+            Ok(())
+        })
+    }
+}
+
+impl<T> Drop for SpinIdOnceCell<T> {
+    #[inline]
+    fn drop(&mut self) {
+        if self.get_mut().is_some() {
+            unsafe { (&mut *self.extra.get()).assume_init_drop() };
+        }
+    }
+}
diff --git a/library/std/src/sys/itron/task.rs b/library/std/src/sys/itron/task.rs
new file mode 100644 (file)
index 0000000..94beb50
--- /dev/null
@@ -0,0 +1,44 @@
+use super::{
+    abi,
+    error::{fail, fail_aborting, ItronError},
+};
+
+use crate::mem::MaybeUninit;
+
+/// Get the ID of the task in Running state. Panics on failure.
+#[inline]
+pub fn current_task_id() -> abi::ID {
+    try_current_task_id().unwrap_or_else(|e| fail(e, &"get_tid"))
+}
+
+/// Get the ID of the task in Running state. Aborts on failure.
+#[inline]
+pub fn current_task_id_aborting() -> abi::ID {
+    try_current_task_id().unwrap_or_else(|e| fail_aborting(e, &"get_tid"))
+}
+
+/// Get the ID of the task in Running state.
+#[inline]
+pub fn try_current_task_id() -> Result<abi::ID, ItronError> {
+    unsafe {
+        let mut out = MaybeUninit::uninit();
+        ItronError::err_if_negative(abi::get_tid(out.as_mut_ptr()))?;
+        Ok(out.assume_init())
+    }
+}
+
+/// Get the specified task's priority. Panics on failure.
+#[inline]
+pub fn task_priority(task: abi::ID) -> abi::PRI {
+    try_task_priority(task).unwrap_or_else(|e| fail(e, &"get_pri"))
+}
+
+/// Get the specified task's priority.
+#[inline]
+pub fn try_task_priority(task: abi::ID) -> Result<abi::PRI, ItronError> {
+    unsafe {
+        let mut out = MaybeUninit::uninit();
+        ItronError::err_if_negative(abi::get_pri(task, out.as_mut_ptr()))?;
+        Ok(out.assume_init())
+    }
+}
diff --git a/library/std/src/sys/itron/thread.rs b/library/std/src/sys/itron/thread.rs
new file mode 100644 (file)
index 0000000..4feb9c5
--- /dev/null
@@ -0,0 +1,352 @@
+//! Thread implementation backed by Î¼ITRON tasks. Assumes `acre_tsk` and
+//! `exd_tsk` are available.
+use super::{
+    abi,
+    error::{expect_success, expect_success_aborting, ItronError},
+    task,
+    time::dur2reltims,
+};
+use crate::{
+    cell::UnsafeCell,
+    convert::TryFrom,
+    ffi::CStr,
+    hint, io,
+    mem::ManuallyDrop,
+    sync::atomic::{AtomicUsize, Ordering},
+    sys::thread_local_dtor::run_dtors,
+    time::Duration,
+};
+
+pub struct Thread {
+    inner: ManuallyDrop<Box<ThreadInner>>,
+
+    /// The ID of the underlying task.
+    task: abi::ID,
+}
+
+/// State data shared between a parent thread and child thread. It's dropped on
+/// a transition to one of the final states.
+struct ThreadInner {
+    /// This field is used on thread creation to pass a closure from
+    /// `Thread::new` to the created task.
+    start: UnsafeCell<ManuallyDrop<Box<dyn FnOnce()>>>,
+
+    /// A state machine. Each transition is annotated with `[...]` in the
+    /// source code.
+    ///
+    /// ```text
+    ///
+    ///    <P>: parent, <C>: child, (?): don't-care
+    ///
+    ///       DETACHED (-1)  -------------------->  EXITED (?)
+    ///                        <C>finish/exd_tsk
+    ///          ^
+    ///          |
+    ///          | <P>detach
+    ///          |
+    ///
+    ///       INIT (0)  ----------------------->  FINISHED (-1)
+    ///                        <C>finish
+    ///          |                                    |
+    ///          | <P>join/slp_tsk                    | <P>join/del_tsk
+    ///          |                                    | <P>detach/del_tsk
+    ///          v                                    v
+    ///
+    ///       JOINING                              JOINED (?)
+    ///     (parent_tid)
+    ///                                            ^
+    ///             \                             /
+    ///              \  <C>finish/wup_tsk        / <P>slp_tsk-complete/ter_tsk
+    ///               \                         /                      & del_tsk
+    ///                \                       /
+    ///                 '--> JOIN_FINALIZE ---'
+    ///                          (-1)
+    ///
+    lifecycle: AtomicUsize,
+}
+
+// Safety: The only `!Sync` field, `ThreadInner::start`, is only touched by
+//         the task represented by `ThreadInner`.
+unsafe impl Sync for ThreadInner {}
+
+const LIFECYCLE_INIT: usize = 0;
+const LIFECYCLE_FINISHED: usize = usize::MAX;
+const LIFECYCLE_DETACHED: usize = usize::MAX;
+const LIFECYCLE_JOIN_FINALIZE: usize = usize::MAX;
+const LIFECYCLE_DETACHED_OR_JOINED: usize = usize::MAX;
+const LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE: usize = usize::MAX;
+// there's no single value for `JOINING`
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * crate::mem::size_of::<usize>();
+
+impl Thread {
+    /// # Safety
+    ///
+    /// See `thread::Builder::spawn_unchecked` for safety requirements.
+    pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+        // Inherit the current task's priority
+        let current_task = task::try_current_task_id().map_err(|e| e.as_io_error())?;
+        let priority = task::try_task_priority(current_task).map_err(|e| e.as_io_error())?;
+
+        let inner = Box::new(ThreadInner {
+            start: UnsafeCell::new(ManuallyDrop::new(p)),
+            lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
+        });
+
+        unsafe extern "C" fn trampoline(exinf: isize) {
+            // Safety: `ThreadInner` is alive at this point
+            let inner = unsafe { &*(exinf as *const ThreadInner) };
+
+            // Safety: Since `trampoline` is called only once for each
+            //         `ThreadInner` and only `trampoline` touches `start`,
+            //         `start` contains contents and is safe to mutably borrow.
+            let p = unsafe { ManuallyDrop::take(&mut *inner.start.get()) };
+            p();
+
+            // Fix the current thread's state just in case, so that the
+            // destructors won't abort
+            // Safety: Not really unsafe
+            let _ = unsafe { abi::unl_cpu() };
+            let _ = unsafe { abi::ena_dsp() };
+
+            // Run TLS destructors now because they are not
+            // called automatically for terminated tasks.
+            unsafe { run_dtors() };
+
+            let old_lifecycle = inner
+                .lifecycle
+                .swap(LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE, Ordering::Release);
+
+            match old_lifecycle {
+                LIFECYCLE_DETACHED => {
+                    // [DETACHED â†’ EXITED]
+                    // No one will ever join, so we'll ask the collector task to
+                    // delete the task.
+
+                    // In this case, `inner`'s ownership has been moved to us,
+                    // And we are responsible for dropping it. The acquire
+                    // ordering is not necessary because the parent thread made
+                    // no memory acccess needing synchronization since the call
+                    // to `acre_tsk`.
+                    // Safety: See above.
+                    let _ = unsafe { Box::from_raw(inner as *const _ as *mut ThreadInner) };
+
+                    // Safety: There are no pinned references to the stack
+                    unsafe { terminate_and_delete_current_task() };
+                }
+                LIFECYCLE_INIT => {
+                    // [INIT â†’ FINISHED]
+                    // The parent hasn't decided whether to join or detach this
+                    // thread yet. Whichever option the parent chooses,
+                    // it'll have to delete this task.
+                    // Since the parent might drop `*inner` as soon as it sees
+                    // `FINISHED`, the release ordering must be used in the
+                    // above `swap` call.
+                }
+                parent_tid => {
+                    // Since the parent might drop `*inner` and terminate us as
+                    // soon as it sees `JOIN_FINALIZE`, the release ordering
+                    // must be used in the above `swap` call.
+
+                    // [JOINING â†’ JOIN_FINALIZE]
+                    // Wake up the parent task.
+                    expect_success(
+                        unsafe {
+                            let mut er = abi::wup_tsk(parent_tid as _);
+                            if er == abi::E_QOVR {
+                                // `E_QOVR` indicates there's already
+                                // a parking token
+                                er = abi::E_OK;
+                            }
+                            er
+                        },
+                        &"wup_tsk",
+                    );
+                }
+            }
+        }
+
+        let inner_ptr = (&*inner) as *const ThreadInner;
+
+        let new_task = ItronError::err_if_negative(unsafe {
+            abi::acre_tsk(&abi::T_CTSK {
+                // Activate this task immediately
+                tskatr: abi::TA_ACT,
+                exinf: inner_ptr as abi::EXINF,
+                // The entry point
+                task: Some(trampoline),
+                itskpri: priority,
+                stksz: stack,
+                // Let the kernel allocate the stack,
+                stk: crate::ptr::null_mut(),
+            })
+        })
+        .map_err(|e| e.as_io_error())?;
+
+        Ok(Self { inner: ManuallyDrop::new(inner), task: new_task })
+    }
+
+    pub fn yield_now() {
+        expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq");
+    }
+
+    pub fn set_name(_name: &CStr) {
+        // nope
+    }
+
+    pub fn sleep(dur: Duration) {
+        for timeout in dur2reltims(dur) {
+            expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk");
+        }
+    }
+
+    pub fn join(mut self) {
+        let inner = &*self.inner;
+        // Get the current task ID. Panicking here would cause a resource leak,
+        // so just abort on failure.
+        let current_task = task::current_task_id_aborting();
+        debug_assert!(usize::try_from(current_task).is_ok());
+        debug_assert_ne!(current_task as usize, LIFECYCLE_INIT);
+        debug_assert_ne!(current_task as usize, LIFECYCLE_DETACHED);
+
+        let current_task = current_task as usize;
+
+        match inner.lifecycle.swap(current_task, Ordering::Acquire) {
+            LIFECYCLE_INIT => {
+                // [INIT â†’ JOINING]
+                // The child task will transition the state to `JOIN_FINALIZE`
+                // and wake us up.
+                loop {
+                    expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
+                    // To synchronize with the child task's memory accesses to
+                    // `inner` up to the point of the assignment of
+                    // `JOIN_FINALIZE`, `Ordering::Acquire` must be used for the
+                    // `load`.
+                    if inner.lifecycle.load(Ordering::Acquire) == LIFECYCLE_JOIN_FINALIZE {
+                        break;
+                    }
+                }
+
+                // [JOIN_FINALIZE â†’ JOINED]
+            }
+            LIFECYCLE_FINISHED => {
+                // [FINISHED â†’ JOINED]
+                // To synchronize with the child task's memory accesses to
+                // `inner` up to the point of the assignment of `FINISHED`,
+                // `Ordering::Acquire` must be used for the above `swap` call`.
+            }
+            _ => unsafe { hint::unreachable_unchecked() },
+        }
+
+        // Terminate and delete the task
+        // Safety: `self.task` still represents a task we own (because this
+        //         method or `detach_inner` is called only once for each
+        //         `Thread`). The task indicated that it's safe to delete by
+        //         entering the `FINISHED` or `JOIN_FINALIZE` state.
+        unsafe { terminate_and_delete_task(self.task) };
+
+        // In either case, we are responsible for dropping `inner`.
+        // Safety: The contents of `self.inner` will not be accessed hereafter
+        let _inner = unsafe { ManuallyDrop::take(&mut self.inner) };
+
+        // Skip the destructor (because it would attempt to detach the thread)
+        crate::mem::forget(self);
+    }
+}
+
+impl Drop for Thread {
+    fn drop(&mut self) {
+        // Detach the thread.
+        match self.inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::Acquire) {
+            LIFECYCLE_INIT => {
+                // [INIT â†’ DETACHED]
+                // When the time comes, the child will figure out that no
+                // one will ever join it.
+                // The ownership of `self.inner` is moved to the child thread.
+                // However, the release ordering is not necessary because we
+                // made no memory acccess needing synchronization since the call
+                // to `acre_tsk`.
+            }
+            LIFECYCLE_FINISHED => {
+                // [FINISHED â†’ JOINED]
+                // The task has already decided that we should delete the task.
+                // To synchronize with the child task's memory accesses to
+                // `inner` up to the point of the assignment of `FINISHED`,
+                // the acquire ordering is required for the above `swap` call.
+
+                // Terminate and delete the task
+                // Safety: `self.task` still represents a task we own (because
+                //         this method or `join_inner` is called only once for
+                //         each `Thread`). The task  indicated that it's safe to
+                //         delete by entering the `FINISHED` state.
+                unsafe { terminate_and_delete_task(self.task) };
+
+                // Wwe are responsible for dropping `inner`.
+                // Safety: The contents of `self.inner` will not be accessed
+                //         hereafter
+                unsafe { ManuallyDrop::drop(&mut self.inner) };
+            }
+            _ => unsafe { hint::unreachable_unchecked() },
+        }
+    }
+}
+
+pub mod guard {
+    pub type Guard = !;
+    pub unsafe fn current() -> Option<Guard> {
+        None
+    }
+    pub unsafe fn init() -> Option<Guard> {
+        None
+    }
+}
+
+/// Terminate and delete the specified task.
+///
+/// This function will abort if `deleted_task` refers to the calling task.
+///
+/// It is assumed that the specified task is solely managed by the caller -
+/// i.e., other threads must not "resuscitate" the specified task or delete it
+/// prematurely while this function is still in progress. It is allowed for the
+/// specified task to exit by its own.
+///
+/// # Safety
+///
+/// The task must be safe to terminate. This is in general not true
+/// because there might be pinned references to the task's stack.
+unsafe fn terminate_and_delete_task(deleted_task: abi::ID) {
+    // Terminate the task
+    // Safety: Upheld by the caller
+    match unsafe { abi::ter_tsk(deleted_task) } {
+        // Indicates the task is already dormant, ignore it
+        abi::E_OBJ => {}
+        er => {
+            expect_success_aborting(er, &"ter_tsk");
+        }
+    }
+
+    // Delete the task
+    // Safety: Upheld by the caller
+    expect_success_aborting(unsafe { abi::del_tsk(deleted_task) }, &"del_tsk");
+}
+
+/// Terminate and delete the calling task.
+///
+/// Atomicity is not required - i.e., it can be assumed that other threads won't
+/// `ter_tsk` the calling task while this function is still in progress. (This
+/// property makes it easy to implement this operation on Î¼ITRON-derived kernels
+/// that don't support `exd_tsk`.)
+///
+/// # Safety
+///
+/// The task must be safe to terminate. This is in general not true
+/// because there might be pinned references to the task's stack.
+unsafe fn terminate_and_delete_current_task() -> ! {
+    expect_success_aborting(unsafe { abi::exd_tsk() }, &"exd_tsk");
+    // Safety: `exd_tsk` never returns on success
+    unsafe { crate::hint::unreachable_unchecked() };
+}
+
+pub fn available_concurrency() -> io::Result<crate::num::NonZeroUsize> {
+    super::unsupported()
+}
diff --git a/library/std/src/sys/itron/time.rs b/library/std/src/sys/itron/time.rs
new file mode 100644 (file)
index 0000000..6a992ad
--- /dev/null
@@ -0,0 +1,123 @@
+use super::{abi, error::expect_success};
+use crate::{convert::TryInto, mem::MaybeUninit, time::Duration};
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(abi::SYSTIM);
+
+impl Instant {
+    pub fn now() -> Instant {
+        // Safety: The provided pointer is valid
+        unsafe {
+            let mut out = MaybeUninit::uninit();
+            expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
+            Instant(out.assume_init())
+        }
+    }
+
+    pub const fn zero() -> Instant {
+        Instant(0)
+    }
+
+    pub fn actually_monotonic() -> bool {
+        // There are ways to change the system time
+        false
+    }
+
+    pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+        self.0.checked_sub(other.0).map(|ticks| {
+            // `SYSTIM` is measured in microseconds
+            Duration::from_micros(ticks)
+        })
+    }
+
+    pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+        // `SYSTIM` is measured in microseconds
+        let ticks = other.as_micros();
+
+        Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
+    }
+
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+        // `SYSTIM` is measured in microseconds
+        let ticks = other.as_micros();
+
+        Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
+    }
+}
+
+/// Split `Duration` into zero or more `RELTIM`s.
+#[inline]
+pub fn dur2reltims(dur: Duration) -> impl Iterator<Item = abi::RELTIM> {
+    // `RELTIM` is microseconds
+    let mut ticks = dur.as_micros();
+
+    crate::iter::from_fn(move || {
+        if ticks == 0 {
+            None
+        } else if ticks <= abi::TMAX_RELTIM as u128 {
+            Some(crate::mem::replace(&mut ticks, 0) as abi::RELTIM)
+        } else {
+            ticks -= abi::TMAX_RELTIM as u128;
+            Some(abi::TMAX_RELTIM)
+        }
+    })
+}
+
+/// Split `Duration` into one or more `TMO`s.
+#[inline]
+fn dur2tmos(dur: Duration) -> impl Iterator<Item = abi::TMO> {
+    // `TMO` is microseconds
+    let mut ticks = dur.as_micros();
+    let mut end = false;
+
+    crate::iter::from_fn(move || {
+        if end {
+            None
+        } else if ticks <= abi::TMAX_RELTIM as u128 {
+            end = true;
+            Some(crate::mem::replace(&mut ticks, 0) as abi::TMO)
+        } else {
+            ticks -= abi::TMAX_RELTIM as u128;
+            Some(abi::TMAX_RELTIM)
+        }
+    })
+}
+
+/// Split `Duration` into one or more API calls with timeout.
+#[inline]
+pub fn with_tmos(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER {
+    let mut er = abi::E_TMOUT;
+    for tmo in dur2tmos(dur) {
+        er = f(tmo);
+        if er != abi::E_TMOUT {
+            break;
+        }
+    }
+    er
+}
+
+/// Split `Duration` into one or more API calls with timeout. This function can
+/// handle spurious wakeups.
+#[inline]
+pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER {
+    // `TMO` and `SYSTIM` are microseconds.
+    // Clamp at `SYSTIM::MAX` for performance reasons. This shouldn't cause
+    // a problem in practice. (`u64::MAX` Î¼s â‰ˆ 584942 years)
+    let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM;
+
+    let start = Instant::now().0;
+    let mut elapsed = 0;
+    let mut er = abi::E_TMOUT;
+    while elapsed <= ticks {
+        er = f(elapsed.min(abi::TMAX_RELTIM as abi::SYSTIM) as abi::TMO);
+        if er != abi::E_TMOUT {
+            break;
+        }
+        elapsed = Instant::now().0.wrapping_sub(start);
+    }
+
+    er
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/library/std/src/sys/itron/time/tests.rs b/library/std/src/sys/itron/time/tests.rs
new file mode 100644 (file)
index 0000000..d14035d
--- /dev/null
@@ -0,0 +1,33 @@
+use super::*;
+
+fn reltim2dur(t: u64) -> Duration {
+    Duration::from_micros(t)
+}
+
+#[test]
+fn test_dur2reltims() {
+    assert_eq!(dur2reltims(reltim2dur(0)).collect::<Vec<_>>(), vec![]);
+    assert_eq!(dur2reltims(reltim2dur(42)).collect::<Vec<_>>(), vec![42]);
+    assert_eq!(
+        dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(),
+        vec![abi::TMAX_RELTIM]
+    );
+    assert_eq!(
+        dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(),
+        vec![abi::TMAX_RELTIM, 10000]
+    );
+}
+
+#[test]
+fn test_dur2tmos() {
+    assert_eq!(dur2tmos(reltim2dur(0)).collect::<Vec<_>>(), vec![0]);
+    assert_eq!(dur2tmos(reltim2dur(42)).collect::<Vec<_>>(), vec![42]);
+    assert_eq!(
+        dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(),
+        vec![abi::TMAX_RELTIM]
+    );
+    assert_eq!(
+        dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(),
+        vec![abi::TMAX_RELTIM, 10000]
+    );
+}
index f813587b1b3408a41a0030b935ed4a2e4535c9f9..8b8be6ebc2f55333cf6f68226b1d797dff09f2c1 100644 (file)
@@ -31,6 +31,9 @@
     } else if #[cfg(windows)] {
         mod windows;
         pub use self::windows::*;
+    } else if #[cfg(target_os = "solid_asp3")] {
+        mod solid;
+        pub use self::solid::*;
     } else if #[cfg(target_os = "hermit")] {
         mod hermit;
         pub use self::hermit::*;
diff --git a/library/std/src/sys/solid/abi/fs.rs b/library/std/src/sys/solid/abi/fs.rs
new file mode 100644 (file)
index 0000000..32800bd
--- /dev/null
@@ -0,0 +1,53 @@
+//! `solid_fs.h`
+use crate::os::raw::{c_char, c_int, c_uchar};
+pub use libc::{
+    blksize_t, dev_t, ino_t, off_t, stat, time_t, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR,
+    O_TRUNC, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET, S_IEXEC, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO,
+    S_IFMT, S_IFREG, S_IREAD, S_IWRITE,
+};
+
+pub const O_ACCMODE: c_int = 0x3;
+
+pub const SOLID_MAX_PATH: usize = 256;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct dirent {
+    pub d_ino: ino_t,
+    pub d_type: c_uchar,
+    pub d_name: [c_char; 256usize],
+}
+
+pub const DT_UNKNOWN: c_uchar = 0;
+pub const DT_FIFO: c_uchar = 1;
+pub const DT_CHR: c_uchar = 2;
+pub const DT_DIR: c_uchar = 4;
+pub const DT_BLK: c_uchar = 6;
+pub const DT_REG: c_uchar = 8;
+pub const DT_LNK: c_uchar = 10;
+pub const DT_SOCK: c_uchar = 12;
+pub const DT_WHT: c_uchar = 14;
+
+pub type S_DIR = c_int;
+
+extern "C" {
+    pub fn SOLID_FS_Open(fd: *mut c_int, path: *const c_char, mode: c_int) -> c_int;
+    pub fn SOLID_FS_Close(fd: c_int) -> c_int;
+    pub fn SOLID_FS_Read(fd: c_int, buf: *mut u8, size: usize, result: *mut usize) -> c_int;
+    pub fn SOLID_FS_Write(fd: c_int, buf: *const u8, size: usize, result: *mut usize) -> c_int;
+    pub fn SOLID_FS_Lseek(fd: c_int, offset: off_t, whence: c_int) -> c_int;
+    pub fn SOLID_FS_Sync(fd: c_int) -> c_int;
+    pub fn SOLID_FS_Ftell(fd: c_int, result: *mut off_t) -> c_int;
+    pub fn SOLID_FS_Feof(fd: c_int, result: *mut c_int) -> c_int;
+    pub fn SOLID_FS_Fsize(fd: c_int, result: *mut usize) -> c_int;
+    pub fn SOLID_FS_Truncate(path: *const c_char, size: off_t) -> c_int;
+    pub fn SOLID_FS_OpenDir(path: *const c_char, pDir: *mut S_DIR) -> c_int;
+    pub fn SOLID_FS_CloseDir(dir: S_DIR) -> c_int;
+    pub fn SOLID_FS_ReadDir(dir: S_DIR, dirp: *mut dirent) -> c_int;
+    pub fn SOLID_FS_Stat(path: *const c_char, buf: *mut stat) -> c_int;
+    pub fn SOLID_FS_Unlink(path: *const c_char) -> c_int;
+    pub fn SOLID_FS_Rename(oldpath: *const c_char, newpath: *const c_char) -> c_int;
+    pub fn SOLID_FS_Chmod(path: *const c_char, mode: c_int) -> c_int;
+    pub fn SOLID_FS_Utime(path: *const c_char, time: time_t) -> c_int;
+    pub fn SOLID_FS_Mkdir(path: *const c_char) -> c_int;
+}
diff --git a/library/std/src/sys/solid/abi/mod.rs b/library/std/src/sys/solid/abi/mod.rs
new file mode 100644 (file)
index 0000000..3526440
--- /dev/null
@@ -0,0 +1,92 @@
+use crate::os::raw::c_int;
+
+mod fs;
+pub mod sockets;
+pub use self::fs::*;
+
+pub const SOLID_BP_PROGRAM_EXITED: usize = 15;
+pub const SOLID_BP_CSABORT: usize = 16;
+
+#[inline(always)]
+pub fn breakpoint_program_exited(tid: usize) {
+    unsafe {
+        match () {
+            #[cfg(target_arch = "arm")]
+            () => asm!("bkpt #{}", const SOLID_BP_PROGRAM_EXITED, in("r0") tid),
+            #[cfg(target_arch = "aarch64")]
+            () => asm!("hlt #{}", const SOLID_BP_PROGRAM_EXITED, in("x0") tid),
+        }
+    }
+}
+
+#[inline(always)]
+pub fn breakpoint_abort() {
+    unsafe {
+        match () {
+            #[cfg(target_arch = "arm")]
+            () => asm!("bkpt #{}", const SOLID_BP_CSABORT),
+            #[cfg(target_arch = "aarch64")]
+            () => asm!("hlt #{}", const SOLID_BP_CSABORT),
+        }
+    }
+}
+
+// `solid_types.h`
+pub use super::itron::abi::{ER, ER_ID, E_TMOUT, ID};
+
+pub const SOLID_ERR_NOTFOUND: ER = -1000;
+pub const SOLID_ERR_NOTSUPPORTED: ER = -1001;
+pub const SOLID_ERR_EBADF: ER = -1002;
+pub const SOLID_ERR_INVALIDCONTENT: ER = -1003;
+pub const SOLID_ERR_NOTUSED: ER = -1004;
+pub const SOLID_ERR_ALREADYUSED: ER = -1005;
+pub const SOLID_ERR_OUTOFBOUND: ER = -1006;
+pub const SOLID_ERR_BADSEQUENCE: ER = -1007;
+pub const SOLID_ERR_UNKNOWNDEVICE: ER = -1008;
+pub const SOLID_ERR_BUSY: ER = -1009;
+pub const SOLID_ERR_TIMEOUT: ER = -1010;
+pub const SOLID_ERR_INVALIDACCESS: ER = -1011;
+pub const SOLID_ERR_NOTREADY: ER = -1012;
+
+// `solid_rtc.h`
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct SOLID_RTC_TIME {
+    pub tm_sec: c_int,
+    pub tm_min: c_int,
+    pub tm_hour: c_int,
+    pub tm_mday: c_int,
+    pub tm_mon: c_int,
+    pub tm_year: c_int,
+    pub tm_wday: c_int,
+}
+
+extern "C" {
+    pub fn SOLID_RTC_ReadTime(time: *mut SOLID_RTC_TIME) -> c_int;
+}
+
+// `solid_log.h`
+extern "C" {
+    pub fn SOLID_LOG_write(s: *const u8, l: usize);
+}
+
+// `solid_mem.h`
+extern "C" {
+    pub fn SOLID_TLS_AddDestructor(id: i32, dtor: unsafe extern "C" fn(*mut u8));
+}
+
+// `solid_rng.h`
+extern "C" {
+    pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> c_int;
+}
+
+// `rwlock.h`
+extern "C" {
+    pub fn rwl_loc_rdl(id: ID) -> ER;
+    pub fn rwl_loc_wrl(id: ID) -> ER;
+    pub fn rwl_ploc_rdl(id: ID) -> ER;
+    pub fn rwl_ploc_wrl(id: ID) -> ER;
+    pub fn rwl_unl_rwl(id: ID) -> ER;
+    pub fn rwl_acre_rwl() -> ER_ID;
+    pub fn rwl_del_rwl(id: ID) -> ER;
+}
diff --git a/library/std/src/sys/solid/abi/sockets.rs b/library/std/src/sys/solid/abi/sockets.rs
new file mode 100644 (file)
index 0000000..7c21d0d
--- /dev/null
@@ -0,0 +1,274 @@
+use crate::os::raw::{c_char, c_uint, c_void};
+pub use libc::{c_int, c_long, size_t, ssize_t, suseconds_t, time_t, timeval};
+
+pub const SOLID_NET_ERR_BASE: c_int = -2000;
+pub const EINPROGRESS: c_int = SOLID_NET_ERR_BASE - libc::EINPROGRESS;
+
+pub const AF_INET6: i32 = 10;
+pub const AF_INET: i32 = 2;
+pub const IPPROTO_IP: i32 = 0;
+pub const IPPROTO_IPV6: i32 = 41;
+pub const IPPROTO_TCP: i32 = 6;
+pub const IPV6_ADD_MEMBERSHIP: i32 = 12;
+pub const IPV6_DROP_MEMBERSHIP: i32 = 13;
+pub const IPV6_MULTICAST_LOOP: i32 = 19;
+pub const IPV6_V6ONLY: i32 = 27;
+pub const IP_TTL: i32 = 2;
+pub const IP_MULTICAST_TTL: i32 = 5;
+pub const IP_MULTICAST_LOOP: i32 = 7;
+pub const IP_ADD_MEMBERSHIP: i32 = 3;
+pub const IP_DROP_MEMBERSHIP: i32 = 4;
+pub const SHUT_RD: i32 = 0;
+pub const SHUT_RDWR: i32 = 2;
+pub const SHUT_WR: i32 = 1;
+pub const SOCK_DGRAM: i32 = 2;
+pub const SOCK_STREAM: i32 = 1;
+pub const SOL_SOCKET: i32 = 4095;
+pub const SO_BROADCAST: i32 = 32;
+pub const SO_ERROR: i32 = 4103;
+pub const SO_RCVTIMEO: i32 = 4102;
+pub const SO_REUSEADDR: i32 = 4;
+pub const SO_SNDTIMEO: i32 = 4101;
+pub const SO_LINGER: i32 = 128;
+pub const TCP_NODELAY: i32 = 1;
+pub const MSG_PEEK: c_int = 1;
+pub const FIONBIO: c_long = 0x8008667eu32 as c_long;
+pub const EAI_NONAME: i32 = -2200;
+pub const EAI_SERVICE: i32 = -2201;
+pub const EAI_FAIL: i32 = -2202;
+pub const EAI_MEMORY: i32 = -2203;
+pub const EAI_FAMILY: i32 = -2204;
+
+pub type sa_family_t = u8;
+pub type socklen_t = u32;
+pub type in_addr_t = u32;
+pub type in_port_t = u16;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct in_addr {
+    pub s_addr: in_addr_t,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct in6_addr {
+    pub s6_addr: [u8; 16],
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct ip_mreq {
+    pub imr_multiaddr: in_addr,
+    pub imr_interface: in_addr,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct ipv6_mreq {
+    pub ipv6mr_multiaddr: in6_addr,
+    pub ipv6mr_interface: c_uint,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+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,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct sockaddr {
+    pub sa_len: u8,
+    pub sa_family: sa_family_t,
+    pub sa_data: [c_char; 14usize],
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+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; 8usize],
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct sockaddr_in6 {
+    pub sin6_len: u8,
+    pub sin6_family: sa_family_t,
+    pub sin6_port: in_port_t,
+    pub sin6_flowinfo: u32,
+    pub sin6_addr: in6_addr,
+    pub sin6_scope_id: u32,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct sockaddr_storage {
+    pub s2_len: u8,
+    pub ss_family: sa_family_t,
+    pub s2_data1: [c_char; 2usize],
+    pub s2_data2: [u32; 3usize],
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+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,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct linger {
+    pub l_onoff: c_int,
+    pub l_linger: c_int,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct iovec {
+    pub iov_base: *mut c_void,
+    pub iov_len: usize,
+}
+
+/// This value can be chosen by an application
+pub const SOLID_NET_FD_SETSIZE: usize = 1;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct fd_set {
+    pub num_fds: usize,
+    pub fds: [c_int; SOLID_NET_FD_SETSIZE],
+}
+
+extern "C" {
+    #[link_name = "SOLID_NET_StrError"]
+    pub fn strerror(errnum: c_int) -> *const c_char;
+
+    pub fn SOLID_NET_GetLastError() -> c_int;
+
+    #[link_name = "SOLID_NET_Accept"]
+    pub fn accept(s: c_int, addr: *mut sockaddr, addrlen: *mut socklen_t) -> c_int;
+
+    #[link_name = "SOLID_NET_Bind"]
+    pub fn bind(s: c_int, name: *const sockaddr, namelen: socklen_t) -> c_int;
+
+    #[link_name = "SOLID_NET_Connect"]
+    pub fn connect(s: c_int, name: *const sockaddr, namelen: socklen_t) -> c_int;
+
+    #[link_name = "SOLID_NET_Close"]
+    pub fn close(s: c_int) -> c_int;
+
+    #[link_name = "SOLID_NET_GetPeerName"]
+    pub fn getpeername(s: c_int, name: *mut sockaddr, namelen: *mut socklen_t) -> c_int;
+
+    #[link_name = "SOLID_NET_GetSockName"]
+    pub fn getsockname(s: c_int, name: *mut sockaddr, namelen: *mut socklen_t) -> c_int;
+
+    #[link_name = "SOLID_NET_GetSockOpt"]
+    pub fn getsockopt(
+        s: c_int,
+        level: c_int,
+        optname: c_int,
+        optval: *mut c_void,
+        optlen: *mut socklen_t,
+    ) -> c_int;
+
+    #[link_name = "SOLID_NET_SetSockOpt"]
+    pub fn setsockopt(
+        s: c_int,
+        level: c_int,
+        optname: c_int,
+        optval: *const c_void,
+        optlen: socklen_t,
+    ) -> c_int;
+
+    #[link_name = "SOLID_NET_Ioctl"]
+    pub fn ioctl(s: c_int, cmd: c_long, argp: *mut c_void) -> c_int;
+
+    #[link_name = "SOLID_NET_Listen"]
+    pub fn listen(s: c_int, backlog: c_int) -> c_int;
+
+    #[link_name = "SOLID_NET_Recv"]
+    pub fn recv(s: c_int, mem: *mut c_void, len: size_t, flags: c_int) -> ssize_t;
+
+    #[link_name = "SOLID_NET_Read"]
+    pub fn read(s: c_int, mem: *mut c_void, len: size_t) -> ssize_t;
+
+    #[link_name = "SOLID_NET_Readv"]
+    pub fn readv(s: c_int, bufs: *const iovec, bufcnt: c_int) -> ssize_t;
+
+    #[link_name = "SOLID_NET_RecvFrom"]
+    pub fn recvfrom(
+        s: c_int,
+        mem: *mut c_void,
+        len: size_t,
+        flags: c_int,
+        from: *mut sockaddr,
+        fromlen: *mut socklen_t,
+    ) -> ssize_t;
+
+    #[link_name = "SOLID_NET_Send"]
+    pub fn send(s: c_int, mem: *const c_void, len: size_t, flags: c_int) -> ssize_t;
+
+    #[link_name = "SOLID_NET_SendMsg"]
+    pub fn sendmsg(s: c_int, message: *const msghdr, flags: c_int) -> ssize_t;
+
+    #[link_name = "SOLID_NET_SendTo"]
+    pub fn sendto(
+        s: c_int,
+        mem: *const c_void,
+        len: size_t,
+        flags: c_int,
+        to: *const sockaddr,
+        tolen: socklen_t,
+    ) -> ssize_t;
+
+    #[link_name = "SOLID_NET_Shutdown"]
+    pub fn shutdown(s: c_int, how: c_int) -> c_int;
+
+    #[link_name = "SOLID_NET_Socket"]
+    pub fn socket(domain: c_int, type_: c_int, protocol: c_int) -> c_int;
+
+    #[link_name = "SOLID_NET_Write"]
+    pub fn write(s: c_int, mem: *const c_void, len: size_t) -> ssize_t;
+
+    #[link_name = "SOLID_NET_Writev"]
+    pub fn writev(s: c_int, bufs: *const iovec, bufcnt: c_int) -> ssize_t;
+
+    #[link_name = "SOLID_NET_FreeAddrInfo"]
+    pub fn freeaddrinfo(ai: *mut addrinfo);
+
+    #[link_name = "SOLID_NET_GetAddrInfo"]
+    pub fn getaddrinfo(
+        nodename: *const c_char,
+        servname: *const c_char,
+        hints: *const addrinfo,
+        res: *mut *mut addrinfo,
+    ) -> c_int;
+
+    #[link_name = "SOLID_NET_Select"]
+    pub fn select(
+        maxfdp1: c_int,
+        readset: *mut fd_set,
+        writeset: *mut fd_set,
+        exceptset: *mut fd_set,
+        timeout: *mut timeval,
+    ) -> c_int;
+}
diff --git a/library/std/src/sys/solid/alloc.rs b/library/std/src/sys/solid/alloc.rs
new file mode 100644 (file)
index 0000000..d013bd8
--- /dev/null
@@ -0,0 +1,32 @@
+use crate::{
+    alloc::{GlobalAlloc, Layout, System},
+    sys::common::alloc::{realloc_fallback, MIN_ALIGN},
+};
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+    #[inline]
+    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+        if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+            unsafe { libc::malloc(layout.size()) as *mut u8 }
+        } else {
+            unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 }
+        }
+    }
+
+    #[inline]
+    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+        unsafe { libc::free(ptr as *mut libc::c_void) }
+    }
+
+    #[inline]
+    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+        unsafe {
+            if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
+                libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
+            } else {
+                realloc_fallback(self, ptr, layout, new_size)
+            }
+        }
+    }
+}
diff --git a/library/std/src/sys/solid/env.rs b/library/std/src/sys/solid/env.rs
new file mode 100644 (file)
index 0000000..6855c11
--- /dev/null
@@ -0,0 +1,9 @@
+pub mod os {
+    pub const FAMILY: &str = "itron";
+    pub const OS: &str = "solid";
+    pub const DLL_PREFIX: &str = "";
+    pub const DLL_SUFFIX: &str = ".so";
+    pub const DLL_EXTENSION: &str = "so";
+    pub const EXE_SUFFIX: &str = "";
+    pub const EXE_EXTENSION: &str = "";
+}
diff --git a/library/std/src/sys/solid/error.rs b/library/std/src/sys/solid/error.rs
new file mode 100644 (file)
index 0000000..547b4f3
--- /dev/null
@@ -0,0 +1,55 @@
+use super::{abi, itron, net};
+use crate::io::ErrorKind;
+
+pub use self::itron::error::{expect_success, ItronError as SolidError};
+
+/// Describe the specified SOLID error code. Returns `None` if it's an
+/// undefined error code.
+///
+/// The SOLID error codes are a superset of Î¼ITRON error codes.
+pub fn error_name(er: abi::ER) -> Option<&'static str> {
+    match er {
+        // Success
+        er if er >= 0 => None,
+        er if er < abi::sockets::SOLID_NET_ERR_BASE => net::error_name(er),
+
+        abi::SOLID_ERR_NOTFOUND => Some("not found"),
+        abi::SOLID_ERR_NOTSUPPORTED => Some("not supported"),
+        abi::SOLID_ERR_EBADF => Some("bad flags"),
+        abi::SOLID_ERR_INVALIDCONTENT => Some("invalid content"),
+        abi::SOLID_ERR_NOTUSED => Some("not used"),
+        abi::SOLID_ERR_ALREADYUSED => Some("already used"),
+        abi::SOLID_ERR_OUTOFBOUND => Some("out of bounds"),
+        abi::SOLID_ERR_BADSEQUENCE => Some("bad sequence"),
+        abi::SOLID_ERR_UNKNOWNDEVICE => Some("unknown device"),
+        abi::SOLID_ERR_BUSY => Some("busy"),
+        abi::SOLID_ERR_TIMEOUT => Some("operation timed out"),
+        abi::SOLID_ERR_INVALIDACCESS => Some("invalid access"),
+        abi::SOLID_ERR_NOTREADY => Some("not ready"),
+
+        _ => itron::error::error_name(er),
+    }
+}
+
+pub fn decode_error_kind(er: abi::ER) -> ErrorKind {
+    match er {
+        // Success
+        er if er >= 0 => ErrorKind::Uncategorized,
+        er if er < abi::sockets::SOLID_NET_ERR_BASE => net::decode_error_kind(er),
+
+        abi::SOLID_ERR_NOTFOUND => ErrorKind::NotFound,
+        abi::SOLID_ERR_NOTSUPPORTED => ErrorKind::Unsupported,
+        abi::SOLID_ERR_EBADF => ErrorKind::InvalidInput,
+        abi::SOLID_ERR_INVALIDCONTENT => ErrorKind::InvalidData,
+        // abi::SOLID_ERR_NOTUSED
+        // abi::SOLID_ERR_ALREADYUSED
+        abi::SOLID_ERR_OUTOFBOUND => ErrorKind::InvalidInput,
+        // abi::SOLID_ERR_BADSEQUENCE
+        abi::SOLID_ERR_UNKNOWNDEVICE => ErrorKind::NotFound,
+        // abi::SOLID_ERR_BUSY
+        abi::SOLID_ERR_TIMEOUT => ErrorKind::TimedOut,
+        // abi::SOLID_ERR_INVALIDACCESS
+        // abi::SOLID_ERR_NOTREADY
+        _ => itron::error::decode_error_kind(er),
+    }
+}
diff --git a/library/std/src/sys/solid/fs.rs b/library/std/src/sys/solid/fs.rs
new file mode 100644 (file)
index 0000000..abc60b5
--- /dev/null
@@ -0,0 +1,529 @@
+use super::{abi, error};
+use crate::{
+    ffi::{CStr, CString, OsStr, OsString},
+    fmt,
+    io::{self, IoSlice, IoSliceMut, SeekFrom},
+    mem::MaybeUninit,
+    os::raw::{c_int, c_short},
+    os::solid::ffi::OsStrExt,
+    path::{Path, PathBuf},
+    sync::Arc,
+    sys::time::SystemTime,
+    sys::unsupported,
+};
+
+pub use crate::sys_common::fs::try_exists;
+
+/// A file descriptor.
+#[derive(Clone, Copy)]
+#[rustc_layout_scalar_valid_range_start(0)]
+// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
+// 32-bit c_int. Below is -2, in two's complement, but that only works out
+// because c_int is 32 bits.
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+struct FileDesc {
+    fd: c_int,
+}
+
+impl FileDesc {
+    #[inline]
+    fn new(fd: c_int) -> FileDesc {
+        assert_ne!(fd, -1i32);
+        // Safety: we just asserted that the value is in the valid range and
+        // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
+        unsafe { FileDesc { fd } }
+    }
+
+    #[inline]
+    fn raw(&self) -> c_int {
+        self.fd
+    }
+}
+
+pub struct File {
+    fd: FileDesc,
+}
+
+#[derive(Clone)]
+pub struct FileAttr {
+    stat: abi::stat,
+}
+
+// all DirEntry's will have a reference to this struct
+struct InnerReadDir {
+    dirp: abi::S_DIR,
+    root: PathBuf,
+}
+
+pub struct ReadDir {
+    inner: Arc<InnerReadDir>,
+}
+
+pub struct DirEntry {
+    entry: abi::dirent,
+    inner: Arc<InnerReadDir>,
+}
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {
+    // generic
+    read: bool,
+    write: bool,
+    append: bool,
+    truncate: bool,
+    create: bool,
+    create_new: bool,
+    // system-specific
+    custom_flags: i32,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions(c_short);
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct FileType(c_short);
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+    pub fn size(&self) -> u64 {
+        self.stat.st_size as u64
+    }
+
+    pub fn perm(&self) -> FilePermissions {
+        FilePermissions(self.stat.st_mode)
+    }
+
+    pub fn file_type(&self) -> FileType {
+        FileType(self.stat.st_mode)
+    }
+
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from_time_t(self.stat.st_mtime))
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from_time_t(self.stat.st_atime))
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from_time_t(self.stat.st_ctime))
+    }
+}
+
+impl FilePermissions {
+    pub fn readonly(&self) -> bool {
+        (self.0 & abi::S_IWRITE) == 0
+    }
+
+    pub fn set_readonly(&mut self, readonly: bool) {
+        if readonly {
+            self.0 &= !abi::S_IWRITE;
+        } else {
+            self.0 |= abi::S_IWRITE;
+        }
+    }
+}
+
+impl FileType {
+    pub fn is_dir(&self) -> bool {
+        self.is(abi::S_IFDIR)
+    }
+    pub fn is_file(&self) -> bool {
+        self.is(abi::S_IFREG)
+    }
+    pub fn is_symlink(&self) -> bool {
+        false
+    }
+
+    pub fn is(&self, mode: c_short) -> bool {
+        self.0 & abi::S_IFMT == mode
+    }
+}
+
+pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+    unsafe {
+        let mut dir = MaybeUninit::uninit();
+        error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir(
+            cstr(p)?.as_ptr(),
+            dir.as_mut_ptr(),
+        ))
+        .map_err(|e| e.as_io_error())?;
+        let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() });
+        Ok(ReadDir { inner })
+    }
+}
+
+impl fmt::Debug for ReadDir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
+        // Thus the result will be e g 'ReadDir("/home")'
+        fmt::Debug::fmt(&*self.inner.root, f)
+    }
+}
+
+impl Iterator for ReadDir {
+    type Item = io::Result<DirEntry>;
+
+    fn next(&mut self) -> Option<io::Result<DirEntry>> {
+        unsafe {
+            let mut out_dirent = MaybeUninit::uninit();
+            error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir(
+                self.inner.dirp,
+                out_dirent.as_mut_ptr(),
+            ))
+            .ok()?;
+            Some(Ok(DirEntry { entry: out_dirent.assume_init(), inner: Arc::clone(&self.inner) }))
+        }
+    }
+}
+
+impl Drop for InnerReadDir {
+    fn drop(&mut self) {
+        unsafe { abi::SOLID_FS_CloseDir(self.dirp) };
+    }
+}
+
+impl DirEntry {
+    pub fn path(&self) -> PathBuf {
+        self.inner.root.join(OsStr::from_bytes(
+            unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes(),
+        ))
+    }
+
+    pub fn file_name(&self) -> OsString {
+        OsStr::from_bytes(unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes())
+            .to_os_string()
+    }
+
+    pub fn metadata(&self) -> io::Result<FileAttr> {
+        lstat(&self.path())
+    }
+
+    pub fn file_type(&self) -> io::Result<FileType> {
+        match self.entry.d_type {
+            abi::DT_CHR => Ok(FileType(abi::S_IFCHR)),
+            abi::DT_FIFO => Ok(FileType(abi::S_IFIFO)),
+            abi::DT_REG => Ok(FileType(abi::S_IFREG)),
+            abi::DT_DIR => Ok(FileType(abi::S_IFDIR)),
+            abi::DT_BLK => Ok(FileType(abi::S_IFBLK)),
+            _ => lstat(&self.path()).map(|m| m.file_type()),
+        }
+    }
+}
+
+impl OpenOptions {
+    pub fn new() -> OpenOptions {
+        OpenOptions {
+            // generic
+            read: false,
+            write: false,
+            append: false,
+            truncate: false,
+            create: false,
+            create_new: false,
+            // system-specific
+            custom_flags: 0,
+        }
+    }
+
+    pub fn read(&mut self, read: bool) {
+        self.read = read;
+    }
+    pub fn write(&mut self, write: bool) {
+        self.write = write;
+    }
+    pub fn append(&mut self, append: bool) {
+        self.append = append;
+    }
+    pub fn truncate(&mut self, truncate: bool) {
+        self.truncate = truncate;
+    }
+    pub fn create(&mut self, create: bool) {
+        self.create = create;
+    }
+    pub fn create_new(&mut self, create_new: bool) {
+        self.create_new = create_new;
+    }
+
+    pub fn custom_flags(&mut self, flags: i32) {
+        self.custom_flags = flags;
+    }
+    pub fn mode(&mut self, _mode: u32) {}
+
+    fn get_access_mode(&self) -> io::Result<c_int> {
+        match (self.read, self.write, self.append) {
+            (true, false, false) => Ok(abi::O_RDONLY),
+            (false, true, false) => Ok(abi::O_WRONLY),
+            (true, true, false) => Ok(abi::O_RDWR),
+            (false, _, true) => Ok(abi::O_WRONLY | abi::O_APPEND),
+            (true, _, true) => Ok(abi::O_RDWR | abi::O_APPEND),
+            (false, false, false) => Err(io::Error::from_raw_os_error(libc::EINVAL)),
+        }
+    }
+
+    fn get_creation_mode(&self) -> io::Result<c_int> {
+        match (self.write, self.append) {
+            (true, false) => {}
+            (false, false) => {
+                if self.truncate || self.create || self.create_new {
+                    return Err(io::Error::from_raw_os_error(libc::EINVAL));
+                }
+            }
+            (_, true) => {
+                if self.truncate && !self.create_new {
+                    return Err(io::Error::from_raw_os_error(libc::EINVAL));
+                }
+            }
+        }
+
+        Ok(match (self.create, self.truncate, self.create_new) {
+            (false, false, false) => 0,
+            (true, false, false) => abi::O_CREAT,
+            (false, true, false) => abi::O_TRUNC,
+            (true, true, false) => abi::O_CREAT | abi::O_TRUNC,
+            (_, _, true) => abi::O_CREAT | abi::O_EXCL,
+        })
+    }
+}
+
+fn cstr(path: &Path) -> io::Result<CString> {
+    Ok(CString::new(path.as_os_str().as_bytes())?)
+}
+
+impl File {
+    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+        let flags = opts.get_access_mode()?
+            | opts.get_creation_mode()?
+            | (opts.custom_flags as c_int & !abi::O_ACCMODE);
+        unsafe {
+            let mut fd = MaybeUninit::uninit();
+            error::SolidError::err_if_negative(abi::SOLID_FS_Open(
+                fd.as_mut_ptr(),
+                cstr(path)?.as_ptr(),
+                flags,
+            ))
+            .map_err(|e| e.as_io_error())?;
+            Ok(File { fd: FileDesc::new(fd.assume_init()) })
+        }
+    }
+
+    pub fn file_attr(&self) -> io::Result<FileAttr> {
+        unsupported()
+    }
+
+    pub fn fsync(&self) -> io::Result<()> {
+        self.flush()
+    }
+
+    pub fn datasync(&self) -> io::Result<()> {
+        self.flush()
+    }
+
+    pub fn truncate(&self, _size: u64) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        unsafe {
+            let mut out_num_bytes = MaybeUninit::uninit();
+            error::SolidError::err_if_negative(abi::SOLID_FS_Read(
+                self.fd.raw(),
+                buf.as_mut_ptr(),
+                buf.len(),
+                out_num_bytes.as_mut_ptr(),
+            ))
+            .map_err(|e| e.as_io_error())?;
+            Ok(out_num_bytes.assume_init())
+        }
+    }
+
+    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        crate::io::default_read_vectored(|buf| self.read(buf), bufs)
+    }
+
+    pub fn is_read_vectored(&self) -> bool {
+        false
+    }
+
+    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        unsafe {
+            let mut out_num_bytes = MaybeUninit::uninit();
+            error::SolidError::err_if_negative(abi::SOLID_FS_Write(
+                self.fd.raw(),
+                buf.as_ptr(),
+                buf.len(),
+                out_num_bytes.as_mut_ptr(),
+            ))
+            .map_err(|e| e.as_io_error())?;
+            Ok(out_num_bytes.assume_init())
+        }
+    }
+
+    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        crate::io::default_write_vectored(|buf| self.write(buf), bufs)
+    }
+
+    pub fn is_write_vectored(&self) -> bool {
+        false
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Sync(self.fd.raw()) })
+            .map_err(|e| e.as_io_error())?;
+        Ok(())
+    }
+
+    pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+        let (whence, pos) = match pos {
+            // Casting to `i64` is fine, too large values will end up as
+            // negative which will cause an error in `SOLID_FS_Lseek`.
+            SeekFrom::Start(off) => (abi::SEEK_SET, off as i64),
+            SeekFrom::End(off) => (abi::SEEK_END, off),
+            SeekFrom::Current(off) => (abi::SEEK_CUR, off),
+        };
+        error::SolidError::err_if_negative(unsafe {
+            abi::SOLID_FS_Lseek(self.fd.raw(), pos, whence)
+        })
+        .map_err(|e| e.as_io_error())?;
+
+        // Get the new offset
+        unsafe {
+            let mut out_offset = MaybeUninit::uninit();
+            error::SolidError::err_if_negative(abi::SOLID_FS_Ftell(
+                self.fd.raw(),
+                out_offset.as_mut_ptr(),
+            ))
+            .map_err(|e| e.as_io_error())?;
+            Ok(out_offset.assume_init() as u64)
+        }
+    }
+
+    pub fn duplicate(&self) -> io::Result<File> {
+        unsupported()
+    }
+
+    pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+        unsupported()
+    }
+}
+
+impl Drop for File {
+    fn drop(&mut self) {
+        unsafe { abi::SOLID_FS_Close(self.fd.raw()) };
+    }
+}
+
+impl DirBuilder {
+    pub fn new() -> DirBuilder {
+        DirBuilder {}
+    }
+
+    pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+        error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Mkdir(cstr(p)?.as_ptr()) })
+            .map_err(|e| e.as_io_error())?;
+        Ok(())
+    }
+}
+
+impl fmt::Debug for File {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("File").field("fd", &self.fd.raw()).finish()
+    }
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+    if stat(p)?.file_type().is_dir() {
+        Err(io::Error::new_const(io::ErrorKind::IsADirectory, &"is a directory"))
+    } else {
+        error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
+            .map_err(|e| e.as_io_error())?;
+        Ok(())
+    }
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+    error::SolidError::err_if_negative(unsafe {
+        abi::SOLID_FS_Rename(cstr(old)?.as_ptr(), cstr(new)?.as_ptr())
+    })
+    .map_err(|e| e.as_io_error())?;
+    Ok(())
+}
+
+pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
+    error::SolidError::err_if_negative(unsafe {
+        abi::SOLID_FS_Chmod(cstr(p)?.as_ptr(), perm.0.into())
+    })
+    .map_err(|e| e.as_io_error())?;
+    Ok(())
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+    if stat(p)?.file_type().is_dir() {
+        error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
+            .map_err(|e| e.as_io_error())?;
+        Ok(())
+    } else {
+        Err(io::Error::new_const(io::ErrorKind::NotADirectory, &"not a directory"))
+    }
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    for child in readdir(path)? {
+        let child = child?;
+        let child_type = child.file_type()?;
+        if child_type.is_dir() {
+            remove_dir_all(&child.path())?;
+        } else {
+            unlink(&child.path())?;
+        }
+    }
+    rmdir(path)
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+    // This target doesn't support symlinks
+    stat(p)?;
+    Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"not a symbolic link"))
+}
+
+pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
+    // This target doesn't support symlinks
+    unsupported()
+}
+
+pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
+    // This target doesn't support symlinks
+    unsupported()
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+    // This target doesn't support symlinks
+    lstat(p)
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+    unsafe {
+        let mut out_stat = MaybeUninit::uninit();
+        error::SolidError::err_if_negative(abi::SOLID_FS_Stat(
+            cstr(p)?.as_ptr(),
+            out_stat.as_mut_ptr(),
+        ))
+        .map_err(|e| e.as_io_error())?;
+        Ok(FileAttr { stat: out_stat.assume_init() })
+    }
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    use crate::fs::File;
+
+    let mut reader = File::open(from)?;
+    let mut writer = File::create(to)?;
+
+    io::copy(&mut reader, &mut writer)
+}
diff --git a/library/std/src/sys/solid/io.rs b/library/std/src/sys/solid/io.rs
new file mode 100644 (file)
index 0000000..9eb17a1
--- /dev/null
@@ -0,0 +1,77 @@
+use crate::marker::PhantomData;
+use crate::slice;
+
+use super::abi::sockets::iovec;
+use libc::c_void;
+
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub struct IoSlice<'a> {
+    vec: iovec,
+    _p: PhantomData<&'a [u8]>,
+}
+
+impl<'a> IoSlice<'a> {
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
+        IoSlice {
+            vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() },
+            _p: PhantomData,
+        }
+    }
+
+    #[inline]
+    pub fn advance(&mut self, n: usize) {
+        if self.vec.iov_len < n {
+            panic!("advancing IoSlice beyond its length");
+        }
+
+        unsafe {
+            self.vec.iov_len -= n;
+            self.vec.iov_base = self.vec.iov_base.add(n);
+        }
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+    }
+}
+
+#[repr(transparent)]
+pub struct IoSliceMut<'a> {
+    vec: iovec,
+    _p: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoSliceMut<'a> {
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
+        IoSliceMut {
+            vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() },
+            _p: PhantomData,
+        }
+    }
+
+    #[inline]
+    pub fn advance(&mut self, n: usize) {
+        if self.vec.iov_len < n {
+            panic!("advancing IoSliceMut beyond its length");
+        }
+
+        unsafe {
+            self.vec.iov_len -= n;
+            self.vec.iov_base = self.vec.iov_base.add(n);
+        }
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+    }
+
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
+    }
+}
diff --git a/library/std/src/sys/solid/memchr.rs b/library/std/src/sys/solid/memchr.rs
new file mode 100644 (file)
index 0000000..452b7a3
--- /dev/null
@@ -0,0 +1,21 @@
+pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+    let p = unsafe {
+        libc::memchr(
+            haystack.as_ptr() as *const libc::c_void,
+            needle as libc::c_int,
+            haystack.len(),
+        )
+    };
+    if p.is_null() { None } else { Some(p as usize - (haystack.as_ptr() as usize)) }
+}
+
+pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+    let p = unsafe {
+        libc::memrchr(
+            haystack.as_ptr() as *const libc::c_void,
+            needle as libc::c_int,
+            haystack.len(),
+        )
+    };
+    if p.is_null() { None } else { Some(p as usize - (haystack.as_ptr() as usize)) }
+}
diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs
new file mode 100644 (file)
index 0000000..211b8d7
--- /dev/null
@@ -0,0 +1,96 @@
+#![allow(dead_code)]
+#![allow(missing_docs, nonstandard_style)]
+#![deny(unsafe_op_in_unsafe_fn)]
+
+mod abi;
+
+#[path = "../itron"]
+mod itron {
+    pub(super) mod abi;
+    pub mod condvar;
+    pub(super) mod error;
+    pub mod mutex;
+    pub(super) mod spin;
+    pub(super) mod task;
+    pub mod thread;
+    pub(super) mod time;
+    use super::unsupported;
+}
+
+pub mod alloc;
+#[path = "../unsupported/args.rs"]
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+// `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as
+// `crate::sys::error`
+pub(crate) mod error;
+pub mod fs;
+pub mod io;
+pub mod net;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+pub mod rwlock;
+pub mod stdio;
+pub use self::itron::{condvar, mutex, thread};
+pub mod memchr;
+pub mod thread_local_dtor;
+pub mod thread_local_key;
+pub mod time;
+
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+
+// SAFETY: must be called only once during runtime cleanup.
+pub unsafe fn cleanup() {}
+
+pub fn unsupported<T>() -> crate::io::Result<T> {
+    Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> crate::io::Error {
+    crate::io::Error::new_const(
+        crate::io::ErrorKind::Unsupported,
+        &"operation not supported on this platform",
+    )
+}
+
+pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind {
+    error::decode_error_kind(code)
+}
+
+#[inline(always)]
+pub fn abort_internal() -> ! {
+    loop {
+        abi::breakpoint_abort();
+    }
+}
+
+// This function is needed by the panic runtime. The symbol is named in
+// pre-link args for the target specification, so keep that in sync.
+#[cfg(not(test))]
+#[no_mangle]
+// NB. used by both libunwind and libpanic_abort
+pub extern "C" fn __rust_abort() {
+    abort_internal();
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+    unsafe {
+        let mut out = crate::mem::MaybeUninit::<[u64; 2]>::uninit();
+        let result = abi::SOLID_RNG_SampleRandomBytes(out.as_mut_ptr() as *mut u8, 16);
+        assert_eq!(result, 0, "SOLID_RNG_SampleRandomBytes failed: {}", result);
+        let [x1, x2] = out.assume_init();
+        (x1, x2)
+    }
+}
+
+pub use libc::strlen;
diff --git a/library/std/src/sys/solid/net.rs b/library/std/src/sys/solid/net.rs
new file mode 100644 (file)
index 0000000..63ba634
--- /dev/null
@@ -0,0 +1,469 @@
+use super::abi;
+use crate::{
+    cmp,
+    ffi::CStr,
+    io::{self, ErrorKind, IoSlice, IoSliceMut},
+    mem,
+    net::{Shutdown, SocketAddr},
+    ptr, str,
+    sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr},
+    sys_common::{AsInner, FromInner, IntoInner},
+    time::Duration,
+};
+
+use self::netc::{sockaddr, socklen_t, MSG_PEEK};
+use libc::{c_int, c_void, size_t};
+
+pub mod netc {
+    pub use super::super::abi::sockets::*;
+}
+
+pub type wrlen_t = size_t;
+
+const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
+
+const fn max_iov() -> usize {
+    // Judging by the source code, it's unlimited, but specify a lower
+    // value just in case.
+    1024
+}
+
+/// A file descriptor.
+#[rustc_layout_scalar_valid_range_start(0)]
+// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
+// 32-bit c_int. Below is -2, in two's complement, but that only works out
+// because c_int is 32 bits.
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+struct FileDesc {
+    fd: c_int,
+}
+
+impl FileDesc {
+    #[inline]
+    fn new(fd: c_int) -> FileDesc {
+        assert_ne!(fd, -1i32);
+        // Safety: we just asserted that the value is in the valid range and
+        // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
+        unsafe { FileDesc { fd } }
+    }
+
+    #[inline]
+    fn raw(&self) -> c_int {
+        self.fd
+    }
+
+    /// Extracts the actual file descriptor without closing it.
+    #[inline]
+    fn into_raw(self) -> c_int {
+        let fd = self.fd;
+        mem::forget(self);
+        fd
+    }
+
+    fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        let ret = cvt(unsafe {
+            netc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
+        })?;
+        Ok(ret as usize)
+    }
+
+    fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        let ret = cvt(unsafe {
+            netc::readv(
+                self.fd,
+                bufs.as_ptr() as *const netc::iovec,
+                cmp::min(bufs.len(), max_iov()) as c_int,
+            )
+        })?;
+        Ok(ret as usize)
+    }
+
+    #[inline]
+    fn is_read_vectored(&self) -> bool {
+        true
+    }
+
+    fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        let ret = cvt(unsafe {
+            netc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
+        })?;
+        Ok(ret as usize)
+    }
+
+    fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        let ret = cvt(unsafe {
+            netc::writev(
+                self.fd,
+                bufs.as_ptr() as *const netc::iovec,
+                cmp::min(bufs.len(), max_iov()) as c_int,
+            )
+        })?;
+        Ok(ret as usize)
+    }
+
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        true
+    }
+
+    fn duplicate(&self) -> io::Result<FileDesc> {
+        super::unsupported()
+    }
+}
+
+impl AsInner<c_int> for FileDesc {
+    fn as_inner(&self) -> &c_int {
+        &self.fd
+    }
+}
+
+impl Drop for FileDesc {
+    fn drop(&mut self) {
+        unsafe { netc::close(self.fd) };
+    }
+}
+
+#[doc(hidden)]
+pub trait IsMinusOne {
+    fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+    ($($t:ident)*) => ($(impl IsMinusOne for $t {
+        fn is_minus_one(&self) -> bool {
+            *self == -1
+        }
+    })*)
+}
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
+pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+    if t.is_minus_one() { Err(last_error()) } else { Ok(t) }
+}
+
+/// A variant of `cvt` for `getaddrinfo` which return 0 for a success.
+pub fn cvt_gai(err: c_int) -> io::Result<()> {
+    if err == 0 {
+        Ok(())
+    } else {
+        let msg: &dyn crate::fmt::Display = match err {
+            netc::EAI_NONAME => &"name or service not known",
+            netc::EAI_SERVICE => &"service not supported",
+            netc::EAI_FAIL => &"non-recoverable failure in name resolution",
+            netc::EAI_MEMORY => &"memory allocation failure",
+            netc::EAI_FAMILY => &"family not supported",
+            _ => &err,
+        };
+        Err(io::Error::new(
+            io::ErrorKind::Uncategorized,
+            &format!("failed to lookup address information: {}", msg)[..],
+        ))
+    }
+}
+
+/// Just to provide the same interface as sys/unix/net.rs
+pub fn cvt_r<T, F>(mut f: F) -> io::Result<T>
+where
+    T: IsMinusOne,
+    F: FnMut() -> T,
+{
+    cvt(f())
+}
+
+/// Returns the last error from the network subsystem.
+fn last_error() -> io::Error {
+    io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() })
+}
+
+pub(super) fn error_name(er: abi::ER) -> Option<&'static str> {
+    unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok()
+}
+
+pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind {
+    let errno = netc::SOLID_NET_ERR_BASE - er;
+    match errno as libc::c_int {
+        libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
+        libc::ECONNRESET => ErrorKind::ConnectionReset,
+        libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
+        libc::EPIPE => ErrorKind::BrokenPipe,
+        libc::ENOTCONN => ErrorKind::NotConnected,
+        libc::ECONNABORTED => ErrorKind::ConnectionAborted,
+        libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
+        libc::EADDRINUSE => ErrorKind::AddrInUse,
+        libc::ENOENT => ErrorKind::NotFound,
+        libc::EINTR => ErrorKind::Interrupted,
+        libc::EINVAL => ErrorKind::InvalidInput,
+        libc::ETIMEDOUT => ErrorKind::TimedOut,
+        libc::EEXIST => ErrorKind::AlreadyExists,
+        libc::ENOSYS => ErrorKind::Unsupported,
+        libc::ENOMEM => ErrorKind::OutOfMemory,
+        libc::EAGAIN => ErrorKind::WouldBlock,
+
+        _ => ErrorKind::Uncategorized,
+    }
+}
+
+pub fn init() {}
+
+pub struct Socket(FileDesc);
+
+impl Socket {
+    pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
+        let fam = match *addr {
+            SocketAddr::V4(..) => netc::AF_INET,
+            SocketAddr::V6(..) => netc::AF_INET6,
+        };
+        Socket::new_raw(fam, ty)
+    }
+
+    pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
+        unsafe {
+            let fd = cvt(netc::socket(fam, ty, 0))?;
+            let fd = FileDesc::new(fd);
+            let socket = Socket(fd);
+
+            Ok(socket)
+        }
+    }
+
+    pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
+        self.set_nonblocking(true)?;
+        let r = unsafe {
+            let (addrp, len) = addr.into_inner();
+            cvt(netc::connect(self.0.raw(), addrp, len))
+        };
+        self.set_nonblocking(false)?;
+
+        match r {
+            Ok(_) => return Ok(()),
+            // there's no ErrorKind for EINPROGRESS
+            Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {}
+            Err(e) => return Err(e),
+        }
+
+        if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
+            return Err(io::Error::new_const(
+                io::ErrorKind::InvalidInput,
+                &"cannot set a 0 duration timeout",
+            ));
+        }
+
+        let mut timeout =
+            netc::timeval { tv_sec: timeout.as_secs() as _, tv_usec: timeout.subsec_micros() as _ };
+        if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+            timeout.tv_usec = 1;
+        }
+
+        let fds = netc::fd_set { num_fds: 1, fds: [self.0.raw()] };
+
+        let mut writefds = fds;
+        let mut errorfds = fds;
+
+        let n = unsafe {
+            cvt(netc::select(
+                self.0.raw() + 1,
+                ptr::null_mut(),
+                &mut writefds,
+                &mut errorfds,
+                &mut timeout,
+            ))?
+        };
+
+        match n {
+            0 => Err(io::Error::new_const(io::ErrorKind::TimedOut, &"connection timed out")),
+            _ => {
+                let can_write = writefds.num_fds != 0;
+                if !can_write {
+                    if let Some(e) = self.take_error()? {
+                        return Err(e);
+                    }
+                }
+                Ok(())
+            }
+        }
+    }
+
+    pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> {
+        let fd = cvt_r(|| unsafe { netc::accept(self.0.raw(), storage, len) })?;
+        let fd = FileDesc::new(fd);
+        Ok(Socket(fd))
+    }
+
+    pub fn duplicate(&self) -> io::Result<Socket> {
+        self.0.duplicate().map(Socket)
+    }
+
+    fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
+        let ret = cvt(unsafe {
+            netc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
+        })?;
+        Ok(ret as usize)
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.recv_with_flags(buf, 0)
+    }
+
+    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.recv_with_flags(buf, MSG_PEEK)
+    }
+
+    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
+    fn recv_from_with_flags(
+        &self,
+        buf: &mut [u8],
+        flags: c_int,
+    ) -> io::Result<(usize, SocketAddr)> {
+        let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() };
+        let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t;
+
+        let n = cvt(unsafe {
+            netc::recvfrom(
+                self.0.raw(),
+                buf.as_mut_ptr() as *mut c_void,
+                buf.len(),
+                flags,
+                &mut storage as *mut _ as *mut _,
+                &mut addrlen,
+            )
+        })?;
+        Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?))
+    }
+
+    pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        self.recv_from_with_flags(buf, 0)
+    }
+
+    pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        self.recv_from_with_flags(buf, MSG_PEEK)
+    }
+
+    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        self.0.write(buf)
+    }
+
+    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
+    pub fn set_timeout(&self, dur: Option<Duration>, kind: c_int) -> io::Result<()> {
+        let timeout = match dur {
+            Some(dur) => {
+                if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
+                    return Err(io::Error::new_const(
+                        io::ErrorKind::InvalidInput,
+                        &"cannot set a 0 duration timeout",
+                    ));
+                }
+
+                let secs = if dur.as_secs() > netc::c_long::MAX as u64 {
+                    netc::c_long::MAX
+                } else {
+                    dur.as_secs() as netc::c_long
+                };
+                let mut timeout = netc::timeval { tv_sec: secs, tv_usec: dur.subsec_micros() as _ };
+                if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+                    timeout.tv_usec = 1;
+                }
+                timeout
+            }
+            None => netc::timeval { tv_sec: 0, tv_usec: 0 },
+        };
+        setsockopt(self, netc::SOL_SOCKET, kind, timeout)
+    }
+
+    pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> {
+        let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?;
+        if raw.tv_sec == 0 && raw.tv_usec == 0 {
+            Ok(None)
+        } else {
+            let sec = raw.tv_sec as u64;
+            let nsec = (raw.tv_usec as u32) * 1000;
+            Ok(Some(Duration::new(sec, nsec)))
+        }
+    }
+
+    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+        let how = match how {
+            Shutdown::Write => netc::SHUT_WR,
+            Shutdown::Read => netc::SHUT_RD,
+            Shutdown::Both => netc::SHUT_RDWR,
+        };
+        cvt(unsafe { netc::shutdown(self.0.raw(), how) })?;
+        Ok(())
+    }
+
+    pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+        let linger = netc::linger {
+            l_onoff: linger.is_some() as netc::c_int,
+            l_linger: linger.unwrap_or_default().as_secs() as netc::c_int,
+        };
+
+        setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger)
+    }
+
+    pub fn linger(&self) -> io::Result<Option<Duration>> {
+        let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?;
+
+        Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
+    }
+
+    pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
+        setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int)
+    }
+
+    pub fn nodelay(&self) -> io::Result<bool> {
+        let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?;
+        Ok(raw != 0)
+    }
+
+    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+        let mut nonblocking = nonblocking as c_int;
+        cvt(unsafe {
+            netc::ioctl(*self.as_inner(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _)
+        })
+        .map(drop)
+    }
+
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?;
+        if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
+    }
+
+    // This method is used by sys_common code to abstract over targets.
+    pub fn as_raw(&self) -> c_int {
+        *self.as_inner()
+    }
+}
+
+impl AsInner<c_int> for Socket {
+    fn as_inner(&self) -> &c_int {
+        self.0.as_inner()
+    }
+}
+
+impl FromInner<c_int> for Socket {
+    fn from_inner(fd: c_int) -> Socket {
+        Socket(FileDesc::new(fd))
+    }
+}
+
+impl IntoInner<c_int> for Socket {
+    fn into_inner(self) -> c_int {
+        self.0.into_raw()
+    }
+}
diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs
new file mode 100644 (file)
index 0000000..82542d8
--- /dev/null
@@ -0,0 +1,200 @@
+use super::unsupported;
+use crate::error::Error as StdError;
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::os::{
+    raw::{c_char, c_int},
+    solid::ffi::{OsStrExt, OsStringExt},
+};
+use crate::path::{self, PathBuf};
+use crate::sys_common::rwlock::StaticRWLock;
+use crate::vec;
+
+use super::{abi, error, itron, memchr};
+
+// `solid` directly maps `errno`s to Î¼ITRON error codes.
+impl itron::error::ItronError {
+    #[inline]
+    pub(crate) fn as_io_error(self) -> crate::io::Error {
+        crate::io::Error::from_raw_os_error(self.as_raw())
+    }
+}
+
+pub fn errno() -> i32 {
+    0
+}
+
+pub fn error_string(errno: i32) -> String {
+    if let Some(name) = error::error_name(errno) { name.to_owned() } else { format!("{}", errno) }
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub struct SplitPaths<'a>(&'a !);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+    panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+    type Item = PathBuf;
+    fn next(&mut self) -> Option<PathBuf> {
+        *self.0
+    }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
+where
+    I: Iterator<Item = T>,
+    T: AsRef<OsStr>,
+{
+    Err(JoinPathsError)
+}
+
+impl fmt::Display for JoinPathsError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        "not supported on this platform yet".fmt(f)
+    }
+}
+
+impl StdError for JoinPathsError {
+    #[allow(deprecated)]
+    fn description(&self) -> &str {
+        "not supported on this platform yet"
+    }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+    unsupported()
+}
+
+static ENV_LOCK: StaticRWLock = StaticRWLock::new();
+
+pub struct Env {
+    iter: vec::IntoIter<(OsString, OsString)>,
+}
+
+impl !Send for Env {}
+impl !Sync for Env {}
+
+impl Iterator for Env {
+    type Item = (OsString, OsString);
+    fn next(&mut self) -> Option<(OsString, OsString)> {
+        self.iter.next()
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
+
+/// Returns a vector of (variable, value) byte-vector pairs for all the
+/// environment variables of the current process.
+pub fn env() -> Env {
+    extern "C" {
+        static mut environ: *const *const c_char;
+    }
+
+    unsafe {
+        let _guard = ENV_LOCK.read();
+        let mut result = Vec::new();
+        if !environ.is_null() {
+            while !(*environ).is_null() {
+                if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+                    result.push(key_value);
+                }
+                environ = environ.add(1);
+            }
+        }
+        return Env { iter: result.into_iter() };
+    }
+
+    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+        // Strategy (copied from glibc): Variable name and value are separated
+        // by an ASCII equals sign '='. Since a variable name must not be
+        // empty, allow variable names starting with an equals sign. Skip all
+        // malformed lines.
+        if input.is_empty() {
+            return None;
+        }
+        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()),
+            )
+        })
+    }
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    // environment variables with a nul byte can't be set, so their value is
+    // always None as well
+    let k = CString::new(k.as_bytes()).ok()?;
+    unsafe {
+        let _guard = ENV_LOCK.read();
+        let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
+        if s.is_null() {
+            None
+        } else {
+            Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
+        }
+    }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    let k = CString::new(k.as_bytes())?;
+    let v = CString::new(v.as_bytes())?;
+
+    unsafe {
+        let _guard = ENV_LOCK.write();
+        cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
+    }
+}
+
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+    let nbuf = CString::new(n.as_bytes())?;
+
+    unsafe {
+        let _guard = ENV_LOCK.write();
+        cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
+    }
+}
+
+/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
+/// function just returns a generic error.
+fn cvt_env(t: c_int) -> io::Result<c_int> {
+    if t == -1 {
+        Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"failure"))
+    } else {
+        Ok(t)
+    }
+}
+
+pub fn temp_dir() -> PathBuf {
+    panic!("no standard temporary directory on this platform")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+    None
+}
+
+pub fn exit(_code: i32) -> ! {
+    let tid = itron::task::try_current_task_id().unwrap_or(0);
+    loop {
+        abi::breakpoint_program_exited(tid as usize);
+    }
+}
+
+pub fn getpid() -> u32 {
+    panic!("no pids on this platform")
+}
diff --git a/library/std/src/sys/solid/path.rs b/library/std/src/sys/solid/path.rs
new file mode 100644 (file)
index 0000000..4a14332
--- /dev/null
@@ -0,0 +1,19 @@
+use crate::ffi::OsStr;
+use crate::path::Prefix;
+
+#[inline]
+pub fn is_sep_byte(b: u8) -> bool {
+    b == b'\\'
+}
+
+#[inline]
+pub fn is_verbatim_sep(b: u8) -> bool {
+    b == b'\\'
+}
+
+pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
+    None
+}
+
+pub const MAIN_SEP_STR: &str = "\\";
+pub const MAIN_SEP: char = '\\';
diff --git a/library/std/src/sys/solid/rwlock.rs b/library/std/src/sys/solid/rwlock.rs
new file mode 100644 (file)
index 0000000..4e39ac2
--- /dev/null
@@ -0,0 +1,92 @@
+//! A readers-writer lock implementation backed by the SOLID kernel extension.
+use super::{
+    abi,
+    itron::{
+        error::{expect_success, expect_success_aborting, fail, ItronError},
+        spin::SpinIdOnceCell,
+    },
+};
+
+pub struct RWLock {
+    /// The ID of the underlying mutex object
+    rwl: SpinIdOnceCell<()>,
+}
+
+pub type MovableRWLock = RWLock;
+
+// Safety: `num_readers` is protected by `mtx_num_readers`
+unsafe impl Send for RWLock {}
+unsafe impl Sync for RWLock {}
+
+fn new_rwl() -> Result<abi::ID, ItronError> {
+    ItronError::err_if_negative(unsafe { abi::rwl_acre_rwl() })
+}
+
+impl RWLock {
+    pub const fn new() -> RWLock {
+        RWLock { rwl: SpinIdOnceCell::new() }
+    }
+
+    /// Get the inner mutex's ID, which is lazily created.
+    fn raw(&self) -> abi::ID {
+        match self.rwl.get_or_try_init(|| new_rwl().map(|id| (id, ()))) {
+            Ok((id, ())) => id,
+            Err(e) => fail(e, &"rwl_acre_rwl"),
+        }
+    }
+
+    #[inline]
+    pub unsafe fn read(&self) {
+        let rwl = self.raw();
+        expect_success(unsafe { abi::rwl_loc_rdl(rwl) }, &"rwl_loc_rdl");
+    }
+
+    #[inline]
+    pub unsafe fn try_read(&self) -> bool {
+        let rwl = self.raw();
+        match unsafe { abi::rwl_ploc_rdl(rwl) } {
+            abi::E_TMOUT => false,
+            er => {
+                expect_success(er, &"rwl_ploc_rdl");
+                true
+            }
+        }
+    }
+
+    #[inline]
+    pub unsafe fn write(&self) {
+        let rwl = self.raw();
+        expect_success(unsafe { abi::rwl_loc_wrl(rwl) }, &"rwl_loc_wrl");
+    }
+
+    #[inline]
+    pub unsafe fn try_write(&self) -> bool {
+        let rwl = self.raw();
+        match unsafe { abi::rwl_ploc_wrl(rwl) } {
+            abi::E_TMOUT => false,
+            er => {
+                expect_success(er, &"rwl_ploc_wrl");
+                true
+            }
+        }
+    }
+
+    #[inline]
+    pub unsafe fn read_unlock(&self) {
+        let rwl = self.raw();
+        expect_success_aborting(unsafe { abi::rwl_unl_rwl(rwl) }, &"rwl_unl_rwl");
+    }
+
+    #[inline]
+    pub unsafe fn write_unlock(&self) {
+        let rwl = self.raw();
+        expect_success_aborting(unsafe { abi::rwl_unl_rwl(rwl) }, &"rwl_unl_rwl");
+    }
+
+    #[inline]
+    pub unsafe fn destroy(&self) {
+        if let Some(rwl) = self.rwl.get().map(|x| x.0) {
+            expect_success_aborting(unsafe { abi::rwl_del_rwl(rwl) }, &"rwl_del_rwl");
+        }
+    }
+}
diff --git a/library/std/src/sys/solid/stdio.rs b/library/std/src/sys/solid/stdio.rs
new file mode 100644 (file)
index 0000000..50f0176
--- /dev/null
@@ -0,0 +1,80 @@
+use super::abi;
+use crate::io;
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+struct PanicOutput;
+
+impl Stdin {
+    pub const fn new() -> Stdin {
+        Stdin
+    }
+}
+
+impl io::Read for Stdin {
+    fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
+        Ok(0)
+    }
+}
+
+impl Stdout {
+    pub const fn new() -> Stdout {
+        Stdout
+    }
+}
+
+impl io::Write for Stdout {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+impl Stderr {
+    pub const fn new() -> Stderr {
+        Stderr
+    }
+}
+
+impl io::Write for Stderr {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+impl PanicOutput {
+    pub const fn new() -> PanicOutput {
+        PanicOutput
+    }
+}
+
+impl io::Write for PanicOutput {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        unsafe { abi::SOLID_LOG_write(buf.as_ptr(), buf.len()) };
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+pub const STDIN_BUF_SIZE: usize = 0;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+    true
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+    Some(PanicOutput::new())
+}
diff --git a/library/std/src/sys/solid/thread_local_dtor.rs b/library/std/src/sys/solid/thread_local_dtor.rs
new file mode 100644 (file)
index 0000000..9735645
--- /dev/null
@@ -0,0 +1,50 @@
+#![cfg(target_thread_local)]
+#![unstable(feature = "thread_local_internals", issue = "none")]
+
+// Simplify dtor registration by using a list of destructors.
+
+use super::{abi, itron::task};
+use crate::cell::Cell;
+use crate::ptr;
+
+#[thread_local]
+static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
+
+type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+    if DTORS.get().is_null() {
+        let tid = task::current_task_id_aborting();
+        let v: Box<List> = box Vec::new();
+        DTORS.set(Box::into_raw(v));
+
+        // Register `tls_dtor` to make sure the TLS destructors are called
+        // for tasks created by other means than `std::thread`
+        unsafe { abi::SOLID_TLS_AddDestructor(tid as i32, tls_dtor) };
+    }
+
+    let list: &mut List = unsafe { &mut *DTORS.get() };
+    list.push((t, dtor));
+}
+
+pub unsafe fn run_dtors() {
+    let ptr = DTORS.get();
+    if !ptr.is_null() {
+        // Swap the destructor list, call all registered destructors,
+        // and repeat this until the list becomes permanently empty.
+        while let Some(list) = Some(crate::mem::replace(unsafe { &mut *ptr }, Vec::new()))
+            .filter(|list| !list.is_empty())
+        {
+            for (ptr, dtor) in list.into_iter() {
+                unsafe { dtor(ptr) };
+            }
+        }
+
+        // Drop the destructor list
+        unsafe { Box::from_raw(DTORS.replace(ptr::null_mut())) };
+    }
+}
+
+unsafe extern "C" fn tls_dtor(_unused: *mut u8) {
+    unsafe { run_dtors() };
+}
diff --git a/library/std/src/sys/solid/thread_local_key.rs b/library/std/src/sys/solid/thread_local_key.rs
new file mode 100644 (file)
index 0000000..b17521f
--- /dev/null
@@ -0,0 +1,26 @@
+pub type Key = usize;
+
+#[inline]
+pub unsafe fn create(_dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
+    panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub unsafe fn set(_key: Key, _value: *mut u8) {
+    panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub unsafe fn get(_key: Key) -> *mut u8 {
+    panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub unsafe fn destroy(_key: Key) {
+    panic!("should not be used on the solid target");
+}
+
+#[inline]
+pub fn requires_synchronized_create() -> bool {
+    panic!("should not be used on the solid target");
+}
diff --git a/library/std/src/sys/solid/time.rs b/library/std/src/sys/solid/time.rs
new file mode 100644 (file)
index 0000000..c67a736
--- /dev/null
@@ -0,0 +1,56 @@
+use super::{abi, error::expect_success};
+use crate::{convert::TryInto, mem::MaybeUninit, time::Duration};
+
+pub use super::itron::time::Instant;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime(abi::time_t);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(0);
+
+impl SystemTime {
+    pub fn now() -> SystemTime {
+        let rtc = unsafe {
+            let mut out = MaybeUninit::zeroed();
+            expect_success(abi::SOLID_RTC_ReadTime(out.as_mut_ptr()), &"SOLID_RTC_ReadTime");
+            out.assume_init()
+        };
+        let t = unsafe {
+            libc::mktime(&mut libc::tm {
+                tm_sec: rtc.tm_sec,
+                tm_min: rtc.tm_min,
+                tm_hour: rtc.tm_hour,
+                tm_mday: rtc.tm_mday,
+                tm_mon: rtc.tm_mon,
+                tm_year: rtc.tm_year,
+                tm_wday: rtc.tm_wday,
+                tm_yday: 0,
+                tm_isdst: 0,
+                tm_gmtoff: 0,
+                tm_zone: crate::ptr::null_mut(),
+            })
+        };
+        assert_ne!(t, -1, "mktime failed");
+        SystemTime(t)
+    }
+
+    pub(super) fn from_time_t(t: abi::time_t) -> Self {
+        Self(t)
+    }
+
+    pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+        if self.0 >= other.0 {
+            Ok(Duration::from_secs((self.0 as u64).wrapping_sub(other.0 as u64)))
+        } else {
+            Err(Duration::from_secs((other.0 as u64).wrapping_sub(self.0 as u64)))
+        }
+    }
+
+    pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+        Some(SystemTime(self.0.checked_add(other.as_secs().try_into().ok()?)?))
+    }
+
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+        Some(SystemTime(self.0.checked_sub(other.as_secs().try_into().ok()?)?))
+    }
+}
index f5424e3d282140abebd04d371147d56e9f4a715f..1c37f4ee4981e51f07cc2d30df4ab60fed59823b 100644 (file)
@@ -120,7 +120,7 @@ unsafe fn sanitize_standard_fds() {
 
     unsafe fn reset_sigpipe() {
         #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
-        assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
+        rtassert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
     }
 }
 
index 133ad3ea420b83b6bceb81d63020619154eb42d5..05f51a46168f6c8a1deb0534f9f83aa627a227ce 100644 (file)
@@ -338,8 +338,17 @@ pub fn available_concurrency() -> io::Result<NonZeroUsize> {
             }
 
             Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
+        } else if #[cfg(target_os = "haiku")] {
+            let mut sinfo: libc::system_info = crate::mem::zeroed();
+            let res = libc::get_system_info(&mut sinfo);
+
+            if res != libc::B_OK {
+                return Err(io::Error::last_os_error());
+            }
+
+            Ok(unsafe { NonZeroUsize::new_unchecked(sinfo.cpu_count as usize) })
         } else {
-            // FIXME: implement on vxWorks, Redox, Haiku, l4re
+            // FIXME: implement on vxWorks, Redox, l4re
             Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Getting the number of hardware threads is not supported on the target platform"))
         }
     }
index 894440564b738d3772dd5c63bbf8a65ee2752c4d..5a5913ebd79a35bbe657c15c524119e6248fd2ea 100644 (file)
@@ -28,8 +28,6 @@
 pub mod mutex;
 pub mod process;
 pub mod remutex;
-#[macro_use]
-pub mod rt;
 pub mod rwlock;
 pub mod thread;
 pub mod thread_info;
diff --git a/library/std/src/sys_common/rt.rs b/library/std/src/sys_common/rt.rs
deleted file mode 100644 (file)
index 02013ec..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-#![deny(unsafe_op_in_unsafe_fn)]
-#![allow(unused_macros)]
-
-use crate::sync::Once;
-use crate::sys;
-use crate::sys_common::thread_info;
-use crate::thread::Thread;
-
-// One-time runtime initialization.
-// Runs before `main`.
-// SAFETY: must be called only once during runtime initialization.
-// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
-#[cfg_attr(test, allow(dead_code))]
-pub unsafe fn init(argc: isize, argv: *const *const u8) {
-    unsafe {
-        sys::init(argc, argv);
-
-        let main_guard = sys::thread::guard::init();
-        // Next, set up the current Thread with the guard information we just
-        // created. Note that this isn't necessary in general for new threads,
-        // but we just do this to name the main thread and to give it correct
-        // info about the stack bounds.
-        let thread = Thread::new(Some("main".to_owned()));
-        thread_info::set(main_guard, thread);
-    }
-}
-
-// One-time runtime cleanup.
-// Runs after `main` or at program exit.
-// NOTE: this is not guaranteed to run, for example when the program aborts.
-#[cfg_attr(test, allow(dead_code))]
-pub fn cleanup() {
-    static CLEANUP: Once = Once::new();
-    CLEANUP.call_once(|| unsafe {
-        // Flush stdout and disable buffering.
-        crate::io::cleanup();
-        // SAFETY: Only called once during runtime cleanup.
-        sys::cleanup();
-    });
-}
-
-// Prints to the "panic output", depending on the platform this may be:
-// - the standard error output
-// - some dedicated platform specific output
-// - nothing (so this macro is a no-op)
-macro_rules! rtprintpanic {
-    ($($t:tt)*) => {
-        if let Some(mut out) = crate::sys::stdio::panic_output() {
-            let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*));
-        }
-    }
-}
-
-macro_rules! rtabort {
-    ($($t:tt)*) => {
-        {
-            rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*));
-            crate::sys::abort_internal();
-        }
-    }
-}
-
-macro_rules! rtassert {
-    ($e:expr) => {
-        if !$e {
-            rtabort!(concat!("assertion failed: ", stringify!($e)));
-        }
-    };
-}
-
-macro_rules! rtunwrap {
-    ($ok:ident, $e:expr) => {
-        match $e {
-            $ok(v) => v,
-            ref err => {
-                let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
-                rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
-            }
-        }
-    };
-}
index f09d16c33e6d6401470b61814af5e2ea39ff27b8..38c9e50009af5eb98193dc559b4dac28834083d3 100644 (file)
@@ -1,4 +1,5 @@
 #![allow(dead_code)] // stack_guard isn't used right now on all platforms
+#![allow(unused_unsafe)] // thread_local with `const {}` triggers this liny
 
 use crate::cell::RefCell;
 use crate::sys::thread::guard::Guard;
@@ -9,7 +10,7 @@ struct ThreadInfo {
     thread: Thread,
 }
 
-thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(None) }
+thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = const { RefCell::new(None) } }
 
 impl ThreadInfo {
     fn with<R, F>(f: F) -> Option<R>
@@ -17,12 +18,13 @@ fn with<R, F>(f: F) -> Option<R>
         F: FnOnce(&mut ThreadInfo) -> R,
     {
         THREAD_INFO
-            .try_with(move |c| {
-                if c.borrow().is_none() {
-                    *c.borrow_mut() =
-                        Some(ThreadInfo { stack_guard: None, thread: Thread::new(None) })
-                }
-                f(c.borrow_mut().as_mut().unwrap())
+            .try_with(move |thread_info| {
+                let mut thread_info = thread_info.borrow_mut();
+                let thread_info = thread_info.get_or_insert_with(|| ThreadInfo {
+                    stack_guard: None,
+                    thread: Thread::new(None),
+                });
+                f(thread_info)
             })
             .ok()
     }
@@ -37,10 +39,9 @@ pub fn stack_guard() -> Option<Guard> {
 }
 
 pub fn set(stack_guard: Option<Guard>, thread: Thread) {
-    THREAD_INFO.with(|c| assert!(c.borrow().is_none()));
-    THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo { stack_guard, thread }));
-}
-
-pub fn reset_guard(stack_guard: Option<Guard>) {
-    THREAD_INFO.with(move |c| c.borrow_mut().as_mut().unwrap().stack_guard = stack_guard);
+    THREAD_INFO.with(move |thread_info| {
+        let mut thread_info = thread_info.borrow_mut();
+        rtassert!(thread_info.is_none());
+        *thread_info = Some(ThreadInfo { stack_guard, thread });
+    });
 }
index f44df845bf4dd56fc2b25a8fdcede67796195647..9d659102b032086aa9255e95a9980b8f3c8397d8 100644 (file)
@@ -457,7 +457,9 @@ pub unsafe fn spawn_unchecked<'a, F, T>(self, f: F) -> io::Result<JoinHandle<T>>
 
         let stack_size = stack_size.unwrap_or_else(thread::min_stack);
 
-        let my_thread = Thread::new(name);
+        let my_thread = Thread::new(name.map(|name| {
+            CString::new(name).expect("thread name may not contain interior null bytes")
+        }));
         let their_thread = my_thread.clone();
 
         let my_packet: Arc<UnsafeCell<Option<Result<T>>>> = Arc::new(UnsafeCell::new(None));
@@ -1073,12 +1075,8 @@ pub struct Thread {
 impl Thread {
     // Used only internally to construct a thread object without spawning
     // Panics if the name contains nuls.
-    pub(crate) fn new(name: Option<String>) -> Thread {
-        let cname =
-            name.map(|n| CString::new(n).expect("thread name may not contain interior null bytes"));
-        Thread {
-            inner: Arc::new(Inner { name: cname, id: ThreadId::new(), parker: Parker::new() }),
-        }
+    pub(crate) fn new(name: Option<CString>) -> Thread {
+        Thread { inner: Arc::new(Inner { name, id: ThreadId::new(), parker: Parker::new() }) }
     }
 
     /// Atomically makes the handle's token available if it is not already.
index a4f7409b3594a5256a6831cbbeeaa52c8b754531..5efd8c9be56333be494db209d4f9e4861b9cd62a 100644 (file)
 /// | UNIX      | [clock_gettime (Monotonic Clock)]                                    |
 /// | Darwin    | [mach_absolute_time]                                                 |
 /// | VXWorks   | [clock_gettime (Monotonic Clock)]                                    |
+/// | SOLID     | `get_tim`                                                            |
 /// | WASI      | [__wasi_clock_time_get (Monotonic Clock)]                            |
 /// | Windows   | [QueryPerformanceCounter]                                            |
 ///
 /// | UNIX      | [clock_gettime (Realtime Clock)]                                     |
 /// | Darwin    | [gettimeofday]                                                       |
 /// | VXWorks   | [clock_gettime (Realtime Clock)]                                     |
+/// | SOLID     | `SOLID_RTC_ReadTime`                                                 |
 /// | WASI      | [__wasi_clock_time_get (Realtime Clock)]                             |
 /// | Windows   | [GetSystemTimePreciseAsFileTime] / [GetSystemTimeAsFileTime]         |
 ///
index aa2448075141935819b27a6f4bfc69a12083c063..04057906d1c3e93c2731193b73a46165bc0b83dd 100644 (file)
@@ -29,7 +29,8 @@ fn write_message(&mut self, s: &str) -> io::Result<()> {
 impl<T: Write> OutputFormatter for JunitFormatter<T> {
     fn write_run_start(&mut self, _test_count: usize) -> io::Result<()> {
         // We write xml header on run start
-        self.write_message(&"<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
+        self.out.write_all(b"\n")?;
+        self.write_message("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
     }
 
     fn write_test_start(&mut self, _desc: &TestDesc) -> io::Result<()> {
@@ -133,6 +134,8 @@ fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
         self.write_message("</testsuite>")?;
         self.write_message("</testsuites>")?;
 
+        self.out.write_all(b"\n\n")?;
+
         Ok(state.failed == 0)
     }
 }
index 06384b159264789108eb7329e14b5eb85437e6d4..25be9e7cc6c0cd217769d902ac6c89b7de8fd09a 100644 (file)
@@ -23,6 +23,7 @@
         unix,
         windows,
         target_os = "psp",
+        target_os = "solid_asp3",
         all(target_vendor = "fortanix", target_env = "sgx"),
     ))] {
         mod libunwind;
index 480b19a5e933685cde31dddb4c6076b1169c2174..053f3e3ee583b9afbe4783715ad4892f80d88569 100644 (file)
@@ -19,6 +19,7 @@ ignore = [
     "library/backtrace",
     "library/stdarch",
     "compiler/rustc_codegen_cranelift",
+    "compiler/rustc_codegen_gcc",
     "src/doc/book",
     "src/doc/edition-guide",
     "src/doc/embedded-book",
index f66f282bea933e0bfbf817f589e74c814eb3fd16..28e7f1fdca7a19519e957f93690c8402abf6697e 100644 (file)
@@ -243,11 +243,16 @@ impl Step for CodegenBackend {
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.paths(&["compiler/rustc_codegen_cranelift", "rustc_codegen_cranelift"])
+        run.paths(&[
+            "compiler/rustc_codegen_cranelift",
+            "rustc_codegen_cranelift",
+            "compiler/rustc_codegen_gcc",
+            "rustc_codegen_gcc",
+        ])
     }
 
     fn make_run(run: RunConfig<'_>) {
-        for &backend in &[INTERNER.intern_str("cranelift")] {
+        for &backend in &[INTERNER.intern_str("cranelift"), INTERNER.intern_str("gcc")] {
             run.builder.ensure(CodegenBackend { target: run.target, backend });
         }
     }
index 9874fdb767f62fdd3d3cb9bb2f8df0dd0c5b85d7..7bc054d3a49fc97a4be9c0fced37855268f1728a 100644 (file)
@@ -10,6 +10,5 @@ bench-stage = 0
 incremental = true
 
 [llvm]
-# Will download LLVM from CI if available on your platform (Linux only for now)
-# https://github.com/rust-lang/rust/issues/77084 tracks support for more platforms
+# Will download LLVM from CI if available on your platform.
 download-ci-llvm = "if-available"
index a5829dfa9d87905192b6331a780e2f397d519431..5bc0a505bf6954602f44fb94b872126e09c15467 100644 (file)
@@ -1,3 +1,4 @@
+use crate::TargetSelection;
 use crate::{t, VERSION};
 use std::fmt::Write as _;
 use std::path::{Path, PathBuf};
@@ -107,6 +108,17 @@ pub fn setup(src_path: &Path, profile: Profile) {
     let include_path = profile.include_path(src_path);
     println!("`x.py` will now use the configuration at {}", include_path.display());
 
+    let build = TargetSelection::from_user(&env!("BUILD_TRIPLE"));
+    let stage_path = ["build", build.rustc_target_arg(), "stage1"].join("/");
+
+    println!();
+
+    if !rustup_installed() && profile != Profile::User {
+        println!("`rustup` is not installed; cannot link `stage1` toolchain");
+    } else if stage_dir_exists(&stage_path[..]) {
+        attempt_toolchain_link(&stage_path[..]);
+    }
+
     let suggestions = match profile {
         Profile::Codegen | Profile::Compiler => &["check", "build", "test"][..],
         Profile::Tools => &[
@@ -139,6 +151,74 @@ pub fn setup(src_path: &Path, profile: Profile) {
     }
 }
 
+fn rustup_installed() -> bool {
+    Command::new("rustup")
+        .arg("--version")
+        .stdout(std::process::Stdio::null())
+        .output()
+        .map_or(false, |output| output.status.success())
+}
+
+fn stage_dir_exists(stage_path: &str) -> bool {
+    match fs::create_dir(&stage_path[..]) {
+        Ok(_) => true,
+        Err(_) => Path::new(&stage_path[..]).exists(),
+    }
+}
+
+fn attempt_toolchain_link(stage_path: &str) {
+    if toolchain_is_linked() {
+        return;
+    }
+
+    if try_link_toolchain(&stage_path[..]) {
+        println!(
+            "Added `stage1` rustup toolchain; try `cargo +stage1 build` on a separate rust project to run a newly-built toolchain"
+        );
+    } else {
+        println!("`rustup` failed to link stage 1 build to `stage1` toolchain");
+        println!(
+            "To manually link stage 1 build to `stage1` toolchain, run:\n
+            `rustup toolchain link stage1 {}`",
+            &stage_path[..]
+        );
+    }
+}
+
+fn toolchain_is_linked() -> bool {
+    match Command::new("rustup")
+        .args(&["toolchain", "list"])
+        .stdout(std::process::Stdio::piped())
+        .output()
+    {
+        Ok(toolchain_list) => {
+            if !String::from_utf8_lossy(&toolchain_list.stdout).contains("stage1") {
+                return false;
+            }
+            // The toolchain has already been linked.
+            println!(
+                "`stage1` toolchain already linked; not attempting to link `stage1` toolchain"
+            );
+        }
+        Err(_) => {
+            // In this case, we don't know if the `stage1` toolchain has been linked;
+            // but `rustup` failed, so let's not go any further.
+            println!(
+                "`rustup` failed to list current toolchains; not attempting to link `stage1` toolchain"
+            );
+        }
+    }
+    true
+}
+
+fn try_link_toolchain(stage_path: &str) -> bool {
+    Command::new("rustup")
+        .stdout(std::process::Stdio::null())
+        .args(&["toolchain", "link", "stage1", &stage_path[..]])
+        .output()
+        .map_or(false, |output| output.status.success())
+}
+
 // Used to get the path for `Subcommand::Setup`
 pub fn interactive_path() -> io::Result<Profile> {
     fn abbrev_all() -> impl Iterator<Item = ((String, String), Profile)> {
index fd29d3a022ad3860be66cbb67bf69415daee65a7..b17398bb6b440741d5e23d84d297ab5122572a1c 100755 (executable)
@@ -61,3 +61,9 @@ elif isWindows && [[ ${CUSTOM_MINGW-0} -ne 1 ]]; then
     ciCommandSetEnv RUST_CONFIGURE_ARGS \
         "${RUST_CONFIGURE_ARGS} --set llvm.clang-cl=$(pwd)/clang-rust/bin/clang-cl.exe"
 fi
+
+if isWindows; then
+    # GitHub image 20210928.2 added LLVM, but it is broken (and we don't want
+    # to use it anyways).
+    rm -rf /c/Program\ Files/LLVM
+fi
index fcb5e0ea68112d85a1d29a7a7335978ef2a02181..eb1282ec444db94055fa9531b6f3f803e86bb382 160000 (submodule)
@@ -1 +1 @@
-Subproject commit fcb5e0ea68112d85a1d29a7a7335978ef2a02181
+Subproject commit eb1282ec444db94055fa9531b6f3f803e86bb382
index fe6227eb3c8533200c52dffa42ef1b6f2f02c40e..2747c4bb2cbc0639b733793ddb0bf4e9daa2634e 160000 (submodule)
@@ -1 +1 @@
-Subproject commit fe6227eb3c8533200c52dffa42ef1b6f2f02c40e
+Subproject commit 2747c4bb2cbc0639b733793ddb0bf4e9daa2634e
index 0e5ed7a4bec065f0cc18c35d1c904639e095314d..13747275bd14c2d2b453100498532f9ae5504769 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 0e5ed7a4bec065f0cc18c35d1c904639e095314d
+Subproject commit 13747275bd14c2d2b453100498532f9ae5504769
index 9d4132b56c4999cd3ce1aeca5f1b2f2cb0d11c24..28aca4a36962c709bce301c03114b5589381dfb8 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 9d4132b56c4999cd3ce1aeca5f1b2f2cb0d11c24
+Subproject commit 28aca4a36962c709bce301c03114b5589381dfb8
index 9198465b6ca8bed669df0cbb67c0e6d0b140803c..d1f03cbaa39d9164f5fe4b9b93762668142e0dad 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 9198465b6ca8bed669df0cbb67c0e6d0b140803c
+Subproject commit d1f03cbaa39d9164f5fe4b9b93762668142e0dad
index bc1873b6836be90eeb3ae50ce219764f5aaf3f39..8c41835183797a7a11a3bc4f03a66fa7aee47eb5 100644 (file)
@@ -14,6 +14,7 @@
 - [Tests](tests/index.md)
 - [Platform Support](platform-support.md)
     - [aarch64-apple-ios-sim](platform-support/aarch64-apple-ios-sim.md)
+    - [\*-kmc-solid_\*](platform-support/kmc-solid.md)
 - [Target Tier Policy](target-tier-policy.md)
 - [Targets](targets/index.md)
     - [Built-in Targets](targets/built-in.md)
index 0f106292e96700edc113db4c114965fb9540e231..e871b08c5b13fe42ee0d9e5df03d4f6df5f74f29 100644 (file)
@@ -202,6 +202,7 @@ target | std | host | notes
 -------|:---:|:----:|-------
 `aarch64-apple-ios-macabi` | ? |  | Apple Catalyst on ARM64
 `aarch64-apple-tvos` | * |  | ARM64 tvOS
+[`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | âœ“ |  | ARM64 SOLID with TOPPERS/ASP3
 `aarch64-unknown-freebsd` | âœ“ | âœ“ | ARM64 FreeBSD
 `aarch64-unknown-hermit` | ? |  |
 `aarch64-unknown-uefi` | * |  | ARM64 UEFI
@@ -222,6 +223,8 @@ target | std | host | notes
 `armv7-unknown-freebsd` | âœ“ | âœ“ | ARMv7 FreeBSD
 `armv7-unknown-netbsd-eabihf` | âœ“ | âœ“ |
 `armv7-wrs-vxworks-eabihf` | ? |  |
+[`armv7a-kmc-solid_asp3-eabi`](platform-support/kmc-solid.md) | âœ“ |  | ARM SOLID with TOPPERS/ASP3
+[`armv7a-kmc-solid_asp3-eabihf`](platform-support/kmc-solid.md) | âœ“ |  | ARM SOLID with TOPPERS/ASP3, hardfloat
 `armv7a-none-eabihf` | * | | ARM Cortex-A, hardfloat
 `armv7s-apple-ios` | âœ“ |  |
 `avr-unknown-gnu-atmega328` | * |  | AVR. Requires `-Z build-std=core`
diff --git a/src/doc/rustc/src/platform-support/kmc-solid.md b/src/doc/rustc/src/platform-support/kmc-solid.md
new file mode 100644 (file)
index 0000000..bbcd0f7
--- /dev/null
@@ -0,0 +1,65 @@
+# \*-kmc-solid_\*
+
+**Tier: 3**
+
+[SOLID] embedded development platform by Kyoto Microcomputer Co., Ltd.
+
+[SOLID]: https://www.kmckk.co.jp/eng/SOLID/
+
+The target names follow this format: `$ARCH-kmc-solid_$KERNEL-$ABI`, where `$ARCH` specifies the target processor architecture, `$KERNEL` the base kernel, and `$ABI` the target ABI (optional). The following targets are currently defined:
+
+|          Target name           | `target_arch` | `target_vendor` | `target_os`  |
+|--------------------------------|---------------|-----------------|--------------|
+| `aarch64-kmc-solid_asp3`       | `aarch64`     | `kmc`           | `solid_asp3` |
+| `armv7a-kmc-solid_asp3-eabi`   | `arm`         | `kmc`           | `solid_asp3` |
+| `armv7a-kmc-solid_asp3-eabihf` | `arm`         | `kmc`           | `solid_asp3` |
+
+## Designated Developers
+
+- [@kawadakk](https://github.com/kawadakk)
+
+## Requirements
+
+This target is cross-compiled.
+A platform-provided C compiler toolchain is required, though it can be substituted by [GNU Arm Embedded Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm) for the purpose of building Rust and functional binaries.
+
+## Building
+
+The target can be built by enabling it for a `rustc` build.
+
+```toml
+[build]
+target = ["aarch64-kmc-solid_asp3"]
+```
+
+Make sure `aarch64-kmc-elf-gcc` is included in `$PATH`. Alternatively, you can use GNU Arm Embedded Toolchain by adding the following to `config.toml`:
+
+```toml
+[target.aarch64-kmc-solid_asp3]
+cc = "arm-none-eabi-gcc"
+```
+
+## Cross-compilation
+
+This target can be cross-compiled from any hosts.
+
+## Testing
+
+Currently there is no support to run the rustc test suite for this target.
+
+## Building Rust programs
+
+Building executables is not supported yet.
+
+If `rustc` has support for that target and the library artifacts are available, then Rust static libraries can be built for that target:
+
+```shell
+$ rustc --target aarch64-kmc-solid_asp3 your-code.rs --crate-type staticlib
+$ ls libyour_code.a
+```
+
+On Rust Nightly it's possible to build without the target artifacts available:
+
+```text
+cargo build -Z build-std --target aarch64-kmc-solid_asp3
+```
index 611a4d08ab2255ee6612a4e81ab583bfd034748d..49fc93f3feabc755340625f814ccb89ce832db86 100644 (file)
@@ -1313,7 +1313,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Type {
         use rustc_hir::*;
 
         match self.kind {
-            TyKind::Never => Never,
+            TyKind::Never => Primitive(PrimitiveType::Never),
             TyKind::Ptr(ref m) => RawPointer(m.mutbl, box m.ty.clean(cx)),
             TyKind::Rptr(ref l, ref m) => {
                 // There are two times a `Fresh` lifetime can be created:
@@ -1402,7 +1402,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Type {
         trace!("cleaning type: {:?}", self);
         let ty = normalize(cx, self).unwrap_or(self);
         match *ty.kind() {
-            ty::Never => Never,
+            ty::Never => Primitive(PrimitiveType::Never),
             ty::Bool => Primitive(PrimitiveType::Bool),
             ty::Char => Primitive(PrimitiveType::Char),
             ty::Int(int_ty) => Primitive(int_ty.into()),
index d139b19f5dc43f3961969b636066e3b4acab1085..257af6ab910163d9bd91b7d2cfa053870c5b6d52 100644 (file)
@@ -11,8 +11,7 @@
 //! This module attempts to reconstruct the original where and/or parameter
 //! bounds by special casing scenarios such as these. Fun!
 
-use std::collections::BTreeMap;
-
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty;
 use rustc_span::Symbol;
 use crate::core::DocContext;
 
 crate fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
-    // First, partition the where clause into its separate components
-    let mut params: BTreeMap<_, (Vec<_>, Vec<_>)> = BTreeMap::new();
+    // First, partition the where clause into its separate components.
+    //
+    // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to
+    // the order of the generated bounds.
+    let mut params: FxIndexMap<Symbol, (Vec<_>, Vec<_>)> = FxIndexMap::default();
     let mut lifetimes = Vec::new();
     let mut equalities = Vec::new();
     let mut tybounds = Vec::new();
index 68a35e55c268e6e3e98a71613f589b8aa9879f64..248ff339514ed2fad3553379a8c2ea804670bf08 100644 (file)
@@ -1396,7 +1396,6 @@ fn def_id_full(&self, cache: &Cache) -> Option<DefId> {
     Slice(Box<Type>),
     /// The `String` field is about the size or the constant representing the array's length.
     Array(Box<Type>, String),
-    Never,
     RawPointer(Mutability, Box<Type>),
     BorrowedRef {
         lifetime: Option<Lifetime>,
@@ -1462,7 +1461,6 @@ impl Type {
             }
             RawPointer(..) => Some(PrimitiveType::RawPointer),
             BareFunction(..) => Some(PrimitiveType::Fn),
-            Never => Some(PrimitiveType::Never),
             _ => None,
         }
     }
@@ -1550,7 +1548,6 @@ fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
                 }
             }
             BareFunction(..) => PrimitiveType::Fn,
-            Never => PrimitiveType::Never,
             Slice(..) => PrimitiveType::Slice,
             Array(..) => PrimitiveType::Array,
             RawPointer(..) => PrimitiveType::RawPointer,
index ddbe68762ee041b6bc3ffdf34d1ee0f6157b7433..074744b3d11e2443cb4f9cf4a2b5a7ce99e8a251 100644 (file)
@@ -25,6 +25,7 @@
 use rustc_span::Span;
 
 use std::cell::RefCell;
+use std::lazy::SyncLazy;
 use std::mem;
 use std::rc::Rc;
 
@@ -271,9 +272,8 @@ impl<'tcx> DocContext<'tcx> {
             providers.typeck_item_bodies = |_, _| {};
             // hack so that `used_trait_imports` won't try to call typeck
             providers.used_trait_imports = |_, _| {
-                lazy_static! {
-                    static ref EMPTY_SET: FxHashSet<LocalDefId> = FxHashSet::default();
-                }
+                static EMPTY_SET: SyncLazy<FxHashSet<LocalDefId>> =
+                    SyncLazy::new(FxHashSet::default);
                 &EMPTY_SET
             };
             // In case typeck does end up being called, don't ICE in case there were name resolution errors
index d11781581a8df6c61daa151e3d22eb257bd1899f..bcd78b2adc08513145b2a0194dc920613cc3d69c 100644 (file)
@@ -761,6 +761,9 @@ fn fmt_type<'cx>(
             fmt::Display::fmt(&tybounds(bounds, lt, cx), f)
         }
         clean::Infer => write!(f, "_"),
+        clean::Primitive(clean::PrimitiveType::Never) => {
+            primitive_link(f, PrimitiveType::Never, "!", cx)
+        }
         clean::Primitive(prim) => primitive_link(f, prim, &*prim.as_sym().as_str(), cx),
         clean::BareFunction(ref decl) => {
             if f.alternate() {
@@ -819,7 +822,6 @@ fn fmt_type<'cx>(
                 primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cx)
             }
         }
-        clean::Never => primitive_link(f, PrimitiveType::Never, "!", cx),
         clean::RawPointer(m, ref t) => {
             let m = match m {
                 hir::Mutability::Mut => "mut",
index ece3ee640e2a69a6f8193a4f2672032b87b8097c..43d1b8f794c3060ea0e9bc68c989539bd545288f 100644 (file)
@@ -9,8 +9,8 @@
 use crate::html::escape::Escape;
 use crate::html::render::Context;
 
+use std::collections::VecDeque;
 use std::fmt::{Display, Write};
-use std::iter::Peekable;
 
 use rustc_lexer::{LiteralKind, TokenKind};
 use rustc_span::edition::Edition;
@@ -201,10 +201,57 @@ fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool)
     })
 }
 
+/// This iterator comes from the same idea than "Peekable" except that it allows to "peek" more than
+/// just the next item by using `peek_next`. The `peek` method always returns the next item after
+/// the current one whereas `peek_next` will return the next item after the last one peeked.
+///
+/// You can use both `peek` and `peek_next` at the same time without problem.
+struct PeekIter<'a> {
+    stored: VecDeque<(TokenKind, &'a str)>,
+    /// This position is reinitialized when using `next`. It is used in `peek_next`.
+    peek_pos: usize,
+    iter: TokenIter<'a>,
+}
+
+impl PeekIter<'a> {
+    fn new(iter: TokenIter<'a>) -> Self {
+        Self { stored: VecDeque::new(), peek_pos: 0, iter }
+    }
+    /// Returns the next item after the current one. It doesn't interfer with `peek_next` output.
+    fn peek(&mut self) -> Option<&(TokenKind, &'a str)> {
+        if self.stored.is_empty() {
+            if let Some(next) = self.iter.next() {
+                self.stored.push_back(next);
+            }
+        }
+        self.stored.front()
+    }
+    /// Returns the next item after the last one peeked. It doesn't interfer with `peek` output.
+    fn peek_next(&mut self) -> Option<&(TokenKind, &'a str)> {
+        self.peek_pos += 1;
+        if self.peek_pos - 1 < self.stored.len() {
+            self.stored.get(self.peek_pos - 1)
+        } else if let Some(next) = self.iter.next() {
+            self.stored.push_back(next);
+            self.stored.back()
+        } else {
+            None
+        }
+    }
+}
+
+impl Iterator for PeekIter<'a> {
+    type Item = (TokenKind, &'a str);
+    fn next(&mut self) -> Option<Self::Item> {
+        self.peek_pos = 0;
+        if let Some(first) = self.stored.pop_front() { Some(first) } else { self.iter.next() }
+    }
+}
+
 /// Processes program tokens, classifying strings of text by highlighting
 /// category (`Class`).
 struct Classifier<'a> {
-    tokens: Peekable<TokenIter<'a>>,
+    tokens: PeekIter<'a>,
     in_attribute: bool,
     in_macro: bool,
     in_macro_nonterminal: bool,
@@ -218,7 +265,7 @@ impl<'a> Classifier<'a> {
     /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
     /// file span which will be used later on by the `span_correspondance_map`.
     fn new(src: &str, edition: Edition, file_span: Span) -> Classifier<'_> {
-        let tokens = TokenIter { src }.peekable();
+        let tokens = PeekIter::new(TokenIter { src });
         Classifier {
             tokens,
             in_attribute: false,
@@ -369,7 +416,7 @@ fn advance(
             // Assume that '&' or '*' is the reference or dereference operator
             // or a reference or pointer type. Unless, of course, it looks like
             // a logical and or a multiplication operator: `&&` or `* `.
-            TokenKind::Star => match lookahead {
+            TokenKind::Star => match self.peek() {
                 Some(TokenKind::Whitespace) => Class::Op,
                 _ => Class::RefKeyWord,
             },
@@ -480,6 +527,9 @@ fn advance(
                 None => match text {
                     "Option" | "Result" => Class::PreludeTy,
                     "Some" | "None" | "Ok" | "Err" => Class::PreludeVal,
+                    // "union" is a weak keyword and is only considered as a keyword when declaring
+                    // a union type.
+                    "union" if self.check_if_is_union_keyword() => Class::KeyWord,
                     _ if self.in_macro_nonterminal => {
                         self.in_macro_nonterminal = false;
                         Class::MacroNonTerminal
@@ -500,7 +550,17 @@ fn advance(
     }
 
     fn peek(&mut self) -> Option<TokenKind> {
-        self.tokens.peek().map(|(toke_kind, _text)| *toke_kind)
+        self.tokens.peek().map(|(token_kind, _text)| *token_kind)
+    }
+
+    fn check_if_is_union_keyword(&mut self) -> bool {
+        while let Some(kind) = self.tokens.peek_next().map(|(token_kind, _text)| token_kind) {
+            if *kind == TokenKind::Whitespace {
+                continue;
+            }
+            return *kind == TokenKind::Ident;
+        }
+        false
     }
 }
 
diff --git a/src/librustdoc/html/highlight/fixtures/union.html b/src/librustdoc/html/highlight/fixtures/union.html
new file mode 100644 (file)
index 0000000..c0acf31
--- /dev/null
@@ -0,0 +1,8 @@
+<span class="kw">union</span> <span class="ident">Foo</span> {
+    <span class="ident">i</span>: <span class="ident">i8</span>,
+    <span class="ident">u</span>: <span class="ident">i8</span>,
+}
+
+<span class="kw">fn</span> <span class="ident">main</span>() {
+    <span class="kw">let</span> <span class="ident">union</span> <span class="op">=</span> <span class="number">0</span>;
+}
diff --git a/src/librustdoc/html/highlight/fixtures/union.rs b/src/librustdoc/html/highlight/fixtures/union.rs
new file mode 100644 (file)
index 0000000..269ee11
--- /dev/null
@@ -0,0 +1,8 @@
+union Foo {
+    i: i8,
+    u: i8,
+}
+
+fn main() {
+    let union = 0;
+}
index 68592ae96c187b273eebb1c5d76c417b0fa86954..450bbfea1ea863143504f6f0a9ec91b77b5cee80 100644 (file)
@@ -54,3 +54,13 @@ fn test_keyword_highlight() {
         expect_file!["fixtures/highlight.html"].assert_eq(&html.into_inner());
     });
 }
+
+#[test]
+fn test_union_highlighting() {
+    create_default_session_globals_then(|| {
+        let src = include_str!("fixtures/union.rs");
+        let mut html = Buffer::new();
+        write_code(&mut html, src, Edition::Edition2018, None);
+        expect_file!["fixtures/union.html"].assert_eq(&html.into_inner());
+    });
+}
index 1c083522beaa901f3409a9de0bb084911100de2c..edd1d8b98fc64b276eabe71eb00c871668c7d407 100644 (file)
@@ -244,7 +244,6 @@ fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option
         | clean::Tuple(_)
         | clean::Slice(_)
         | clean::Array(_, _)
-        | clean::Never
         | clean::RawPointer(_, _)
         | clean::QPath { .. }
         | clean::Infer
index fda9070305797ad417e811466eb329380b924d27..ea81b041c3bc6a973f5f94864d57b2c341cc8f91 100644 (file)
@@ -417,13 +417,13 @@ fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self {
                 }
             }
             Generic(s) => Type::Generic(s.to_string()),
+            Primitive(clean::PrimitiveType::Never) => Type::Never,
             Primitive(p) => Type::Primitive(p.as_sym().to_string()),
             BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))),
             Tuple(t) => Type::Tuple(t.into_iter().map(|x| x.into_tcx(tcx)).collect()),
             Slice(t) => Type::Slice(Box::new((*t).into_tcx(tcx))),
             Array(t, s) => Type::Array { type_: Box::new((*t).into_tcx(tcx)), len: s },
             ImplTrait(g) => Type::ImplTrait(g.into_iter().map(|x| x.into_tcx(tcx)).collect()),
-            Never => Type::Never,
             Infer => Type::Infer,
             RawPointer(mutability, type_) => Type::RawPointer {
                 mutable: mutability == ast::Mutability::Mut,
index 5089cc30a1e398cb907aa45f6e585b0eed81572e..915a9fd2b894a620424f099e633faedc443e9b1c 100644 (file)
@@ -169,6 +169,8 @@ fn item(&mut self, item: clean::Item) -> Result<(), Error> {
                 s.impls = self.get_impls(id.expect_def_id())
             } else if let types::ItemEnum::Enum(ref mut e) = new_item.inner {
                 e.impls = self.get_impls(id.expect_def_id())
+            } else if let types::ItemEnum::Union(ref mut u) = new_item.inner {
+                u.impls = self.get_impls(id.expect_def_id())
             }
             let removed = self.index.borrow_mut().insert(from_item_id(id), new_item.clone());
 
index 5cfd21046f5d7748f64c2f5a034e848939f12077..efc8e31498a9c9c50d09fa75ddf1eb049e03f876 100644 (file)
@@ -18,8 +18,6 @@
 #![recursion_limit = "256"]
 #![warn(rustc::internal)]
 
-#[macro_use]
-extern crate lazy_static;
 #[macro_use]
 extern crate tracing;
 
index a64c960b9f716bf27f739eb6b3937c82f20fe164..bec0fa9c0495722bfa36bae10fe39d58c034ff19 100644 (file)
@@ -1,7 +1,7 @@
 // MIR for `BAR::promoted[0]` after SimplifyCfg-elaborate-drops
 
 promoted[0] in BAR: &[&i32; 1] = {
-    let mut _0: &[&i32; 1];              // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+    let mut _0: &[&i32; 1];              // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
     let mut _1: [&i32; 1];               // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
     let mut _2: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
     let mut _3: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
@@ -16,8 +16,8 @@ promoted[0] in BAR: &[&i32; 1] = {
                                          // + literal: Const { ty: &i32, val: Value(Scalar(alloc1)) }
         _2 = &(*_3);                     // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
         _1 = [move _2];                  // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
-        _0 = &_1;                        // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
-        return;                          // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+        _0 = &_1;                        // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
+        return;                          // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
     }
 }
 
index 49d0a7ff455030181b4c27bd926b51ad958f675d..bdd62f1029f51ddb55ca0dfda88daa978f240cf5 100644 (file)
@@ -3,21 +3,21 @@
   
   static mut BAR: *const &i32 = {
       let mut _0: *const &i32;             // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:17: 9:28
-      let mut _1: &[&i32];                 // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
-      let mut _2: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+      let mut _1: &[&i32];                 // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
+      let mut _2: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
       let _3: [&i32; 1];                   // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
       let mut _4: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
       let _5: &i32;                        // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
-+     let mut _6: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
++     let mut _6: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
   
       bb0: {
-          StorageLive(_1);                 // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
-          StorageLive(_2);                 // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+          StorageLive(_1);                 // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
+          StorageLive(_2);                 // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
 -         StorageLive(_3);                 // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
 -         StorageLive(_4);                 // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
 -         StorageLive(_5);                 // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
 -         _5 = const {alloc1: &i32};       // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
-+         _6 = const BAR::promoted[0];     // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
++         _6 = const BAR::promoted[0];     // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
                                            // ty::Const
 -                                          // + ty: &i32
 -                                          // + val: Value(Scalar(alloc1))
 -                                          // + literal: Const { ty: &i32, val: Value(Scalar(alloc1)) }
 -         _4 = &(*_5);                     // scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
 -         _3 = [move _4];                  // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
--         _2 = &_3;                        // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
-+                                          // + span: $DIR/const-promotion-extern-static.rs:9:31: 9:35
+-         _2 = &_3;                        // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
++                                          // + span: $DIR/const-promotion-extern-static.rs:9:31: 9:44
 +                                          // + literal: Const { ty: &[&i32; 1], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:6 ~ const_promotion_extern_static[55e6]::BAR), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
-+         _2 = &(*_6);                     // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
-          _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
++         _2 = &(*_6);                     // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
+          _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
 -         StorageDead(_4);                 // scope 0 at $DIR/const-promotion-extern-static.rs:9:34: 9:35
           StorageDead(_2);                 // scope 0 at $DIR/const-promotion-extern-static.rs:9:34: 9:35
           _0 = core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
index 8b3c5d332f2514ebfed3247cb93f5d2a22f7fbcb..c01b31525b6a6ee0ffa906bf53f9f30cfb558f4d 100644 (file)
@@ -1,7 +1,7 @@
 // MIR for `FOO::promoted[0]` after SimplifyCfg-elaborate-drops
 
 promoted[0] in FOO: &[&i32; 1] = {
-    let mut _0: &[&i32; 1];              // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+    let mut _0: &[&i32; 1];              // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
     let mut _1: [&i32; 1];               // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
     let mut _2: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
     let mut _3: *const i32;              // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
@@ -16,8 +16,8 @@ promoted[0] in FOO: &[&i32; 1] = {
                                          // + literal: Const { ty: *const i32, val: Value(Scalar(alloc3)) }
         _2 = &(*_3);                     // scope 0 at $DIR/const-promotion-extern-static.rs:13:41: 13:43
         _1 = [move _2];                  // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
-        _0 = &_1;                        // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
-        return;                          // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+        _0 = &_1;                        // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
+        return;                          // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
     }
 }
 
index f2504ae880eab2944f991db4ba81cfe0542cc458..94b337806aff8f19f4acd166eecaa52f45fe150b 100644 (file)
@@ -3,23 +3,23 @@
   
   static mut FOO: *const &i32 = {
       let mut _0: *const &i32;             // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:17: 13:28
-      let mut _1: &[&i32];                 // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
-      let mut _2: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+      let mut _1: &[&i32];                 // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
+      let mut _2: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
       let _3: [&i32; 1];                   // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
       let mut _4: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
       let _5: *const i32;                  // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
-+     let mut _6: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
++     let mut _6: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
       scope 1 {
       }
   
       bb0: {
-          StorageLive(_1);                 // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
-          StorageLive(_2);                 // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+          StorageLive(_1);                 // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
+          StorageLive(_2);                 // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
 -         StorageLive(_3);                 // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
 -         StorageLive(_4);                 // scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
 -         StorageLive(_5);                 // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
 -         _5 = const {alloc3: *const i32}; // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
-+         _6 = const FOO::promoted[0];     // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
++         _6 = const FOO::promoted[0];     // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
                                            // ty::Const
 -                                          // + ty: *const i32
 -                                          // + val: Value(Scalar(alloc3))
 -                                          // + literal: Const { ty: *const i32, val: Value(Scalar(alloc3)) }
 -         _4 = &(*_5);                     // scope 1 at $DIR/const-promotion-extern-static.rs:13:41: 13:43
 -         _3 = [move _4];                  // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
--         _2 = &_3;                        // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
-+                                          // + span: $DIR/const-promotion-extern-static.rs:13:31: 13:46
+-         _2 = &_3;                        // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
++                                          // + span: $DIR/const-promotion-extern-static.rs:13:31: 13:55
 +                                          // + literal: Const { ty: &[&i32; 1], val: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:7 ~ const_promotion_extern_static[55e6]::FOO), const_param_did: None }, substs_: Some([]), promoted: Some(promoted[0]) }) }
-+         _2 = &(*_6);                     // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
-          _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
++         _2 = &(*_6);                     // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
+          _1 = move _2 as &[&i32] (Pointer(Unsize)); // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
 -         StorageDead(_4);                 // scope 0 at $DIR/const-promotion-extern-static.rs:13:45: 13:46
           StorageDead(_2);                 // scope 0 at $DIR/const-promotion-extern-static.rs:13:45: 13:46
           _0 = core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
index 48a37a8496cb720ae8a842ee0dafa0a10f649d10..7695afded3da367a0f295a3ecf49f3fb75599412 100644 (file)
@@ -5,7 +5,7 @@
       debug s => _1;                       // in scope 0 at $DIR/deduplicate_blocks.rs:2:36: 2:37
       let mut _0: bool;                    // return place in scope 0 at $DIR/deduplicate_blocks.rs:2:48: 2:52
       let mut _2: &[u8];                   // in scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
-      let mut _3: &str;                    // in scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:12
+      let mut _3: &str;                    // in scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
       let mut _4: usize;                   // in scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
       let mut _5: bool;                    // in scope 0 at $DIR/deduplicate_blocks.rs:5:9: 5:31
       let mut _6: usize;                   // in scope 0 at $DIR/deduplicate_blocks.rs:4:9: 4:37
@@ -19,8 +19,8 @@
   
       bb0: {
           StorageLive(_2);                 // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
-          StorageLive(_3);                 // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:12
-          _3 = _1;                         // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:12
+          StorageLive(_3);                 // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
+          _3 = _1;                         // scope 0 at $DIR/deduplicate_blocks.rs:3:11: 3:23
           StorageLive(_8);                 // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
           _8 = _3;                         // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
 -         _2 = transmute::<&str, &[u8]>(move _8) -> bb14; // scope 2 at $DIR/deduplicate_blocks.rs:3:11: 3:23
index fd29e14a04161bcd01daa38ed6377377c7278baf..000bc6343257b57ad4b9bcec27fc3ad2ef7bd860 100644 (file)
@@ -7,9 +7,9 @@
       debug upper => _3;                   // in scope 0 at $DIR/funky_arms.rs:11:69: 11:74
       let mut _0: std::result::Result<(), std::fmt::Error>; // return place in scope 0 at $DIR/funky_arms.rs:11:85: 11:91
       let _4: bool;                        // in scope 0 at $DIR/funky_arms.rs:15:9: 15:19
-      let mut _5: &std::fmt::Formatter;    // in scope 0 at $DIR/funky_arms.rs:15:22: 15:25
+      let mut _5: &std::fmt::Formatter;    // in scope 0 at $DIR/funky_arms.rs:15:22: 15:37
       let mut _7: std::option::Option<usize>; // in scope 0 at $DIR/funky_arms.rs:24:30: 24:45
-      let mut _8: &std::fmt::Formatter;    // in scope 0 at $DIR/funky_arms.rs:24:30: 24:33
+      let mut _8: &std::fmt::Formatter;    // in scope 0 at $DIR/funky_arms.rs:24:30: 24:45
       let mut _9: isize;                   // in scope 0 at $DIR/funky_arms.rs:24:12: 24:27
       let mut _11: &mut std::fmt::Formatter; // in scope 0 at $DIR/funky_arms.rs:26:43: 26:46
       let mut _12: &T;                     // in scope 0 at $DIR/funky_arms.rs:26:48: 26:51
@@ -36,8 +36,8 @@
   
       bb0: {
           StorageLive(_4);                 // scope 0 at $DIR/funky_arms.rs:15:9: 15:19
-          StorageLive(_5);                 // scope 0 at $DIR/funky_arms.rs:15:22: 15:25
-          _5 = &(*_1);                     // scope 0 at $DIR/funky_arms.rs:15:22: 15:25
+          StorageLive(_5);                 // scope 0 at $DIR/funky_arms.rs:15:22: 15:37
+          _5 = &(*_1);                     // scope 0 at $DIR/funky_arms.rs:15:22: 15:37
           _4 = Formatter::sign_plus(move _5) -> bb1; // scope 0 at $DIR/funky_arms.rs:15:22: 15:37
                                            // mir::Constant
                                            // + span: $DIR/funky_arms.rs:15:26: 15:35
@@ -62,8 +62,8 @@
   
       bb4: {
           StorageLive(_7);                 // scope 2 at $DIR/funky_arms.rs:24:30: 24:45
-          StorageLive(_8);                 // scope 2 at $DIR/funky_arms.rs:24:30: 24:33
-          _8 = &(*_1);                     // scope 2 at $DIR/funky_arms.rs:24:30: 24:33
+          StorageLive(_8);                 // scope 2 at $DIR/funky_arms.rs:24:30: 24:45
+          _8 = &(*_1);                     // scope 2 at $DIR/funky_arms.rs:24:30: 24:45
           _7 = Formatter::precision(move _8) -> bb5; // scope 2 at $DIR/funky_arms.rs:24:30: 24:45
                                            // mir::Constant
                                            // + span: $DIR/funky_arms.rs:24:34: 24:43
index 3bdd4f4ff56cc7583aba6815e8917ef57761a416..7379d5f219c48fb47fb34574884acc9380fce73a 100644 (file)
@@ -4,13 +4,13 @@
   fn clone(_1: fn(A, B)) -> fn(A, B) {
       debug f => _1;                       // in scope 0 at $DIR/inline-shims.rs:5:20: 5:21
       let mut _0: fn(A, B);                // return place in scope 0 at $DIR/inline-shims.rs:5:36: 5:44
-      let mut _2: &fn(A, B);               // in scope 0 at $DIR/inline-shims.rs:6:5: 6:6
+      let mut _2: &fn(A, B);               // in scope 0 at $DIR/inline-shims.rs:6:5: 6:14
 +     scope 1 (inlined <fn(A, B) as Clone>::clone - shim(fn(A, B))) { // at $DIR/inline-shims.rs:6:5: 6:14
 +     }
   
       bb0: {
-          StorageLive(_2);                 // scope 0 at $DIR/inline-shims.rs:6:5: 6:6
-          _2 = &_1;                        // scope 0 at $DIR/inline-shims.rs:6:5: 6:6
+          StorageLive(_2);                 // scope 0 at $DIR/inline-shims.rs:6:5: 6:14
+          _2 = &_1;                        // scope 0 at $DIR/inline-shims.rs:6:5: 6:14
 -         _0 = <fn(A, B) as Clone>::clone(move _2) -> bb1; // scope 0 at $DIR/inline-shims.rs:6:5: 6:14
 -                                          // mir::Constant
 -                                          // + span: $DIR/inline-shims.rs:6:7: 6:12
index eada5ac13476e072cceca649cfa0a7eedb2267f4..0be979901ac0338ef4e5b708d2f98eba0c052648 100644 (file)
@@ -3,11 +3,11 @@
 fn test(_1: &dyn X) -> u32 {
     debug x => _1;                       // in scope 0 at $DIR/inline-trait-method.rs:8:9: 8:10
     let mut _0: u32;                     // return place in scope 0 at $DIR/inline-trait-method.rs:8:23: 8:26
-    let mut _2: &dyn X;                  // in scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6
+    let mut _2: &dyn X;                  // in scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10
 
     bb0: {
-        StorageLive(_2);                 // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6
-        _2 = &(*_1);                     // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:6
+        StorageLive(_2);                 // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10
+        _2 = &(*_1);                     // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10
         _0 = <dyn X as X>::y(move _2) -> bb1; // scope 0 at $DIR/inline-trait-method.rs:9:5: 9:10
                                          // mir::Constant
                                          // + span: $DIR/inline-trait-method.rs:9:7: 9:8
index 651855f80245418935b01b0515fc6e4a607263d5..1b5153daa8ba8c52e654ba5aab258be33b84baf2 100644 (file)
@@ -5,7 +5,7 @@ fn a(_1: &mut [T]) -> &mut [T] {
     let mut _0: &mut [T];                // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:2:29: 2:37
     let mut _2: &mut [T];                // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
     let mut _3: &mut [T];                // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
-    let mut _4: &mut [T];                // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6
+    let mut _4: &mut [T];                // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
     scope 1 (inlined <[T] as AsMut<[T]>>::as_mut) { // at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
         debug self => _4;                // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
         let mut _5: &mut [T];            // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
@@ -14,8 +14,8 @@ fn a(_1: &mut [T]) -> &mut [T] {
     bb0: {
         StorageLive(_2);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
         StorageLive(_3);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
-        StorageLive(_4);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6
-        _4 = &mut (*_1);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6
+        StorageLive(_4);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
+        _4 = &mut (*_1);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
         StorageLive(_5);                 // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
         _5 = &mut (*_4);                 // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
         _3 = &mut (*_5);                 // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15
index c67ea7e00b7609e88d0d26c78e9be59769c1666f..257ddec780e93668ea42bb3cd3e0bcbc9b84f9c4 100644 (file)
@@ -5,7 +5,7 @@ fn b(_1: &mut Box<T>) -> &mut T {
     let mut _0: &mut T;                  // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:7:32: 7:38
     let mut _2: &mut T;                  // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
     let mut _3: &mut T;                  // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
-    let mut _4: &mut std::boxed::Box<T>; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6
+    let mut _4: &mut std::boxed::Box<T>; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
     scope 1 (inlined <Box<T> as AsMut<T>>::as_mut) { // at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
         debug self => _4;                // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
         let mut _5: &mut T;              // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
@@ -15,8 +15,8 @@ fn b(_1: &mut Box<T>) -> &mut T {
     bb0: {
         StorageLive(_2);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
         StorageLive(_3);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
-        StorageLive(_4);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6
-        _4 = &mut (*_1);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6
+        StorageLive(_4);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
+        _4 = &mut (*_1);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
         StorageLive(_5);                 // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
         StorageLive(_6);                 // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
         _6 = &mut (*(*_4));              // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15
index 16fae453ac936c53d58aaa0d9a3b7eea1441e78f..9817e8cd5fa4d51cc07a78bc8c643841234ce917 100644 (file)
@@ -4,15 +4,15 @@ fn c(_1: &[T]) -> &[T] {
     debug x => _1;                       // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:12:13: 12:14
     let mut _0: &[T];                    // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:12:25: 12:29
     let _2: &[T];                        // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
-    let mut _3: &[T];                    // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6
+    let mut _3: &[T];                    // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
     scope 1 (inlined <[T] as AsRef<[T]>>::as_ref) { // at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
         debug self => _3;                // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
     }
 
     bb0: {
         StorageLive(_2);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
-        StorageLive(_3);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6
-        _3 = &(*_1);                     // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:6
+        StorageLive(_3);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
+        _3 = &(*_1);                     // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
         _2 = _3;                         // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
         _0 = &(*_2);                     // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:5: 13:15
         StorageDead(_3);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:13:14: 13:15
index e9ca7095a43ab49f1772f7e0ea57853b9ee25c41..e49c91581b3cd5add76f405bd6aee272fb4f10c1 100644 (file)
@@ -4,15 +4,15 @@ fn d(_1: &Box<T>) -> &T {
     debug x => _1;                       // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:17:13: 17:14
     let mut _0: &T;                      // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:17:28: 17:30
     let _2: &T;                          // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
-    let mut _3: &std::boxed::Box<T>;     // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6
+    let mut _3: &std::boxed::Box<T>;     // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
     scope 1 (inlined <Box<T> as AsRef<T>>::as_ref) { // at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
         debug self => _3;                // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
     }
 
     bb0: {
         StorageLive(_2);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
-        StorageLive(_3);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6
-        _3 = &(*_1);                     // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:6
+        StorageLive(_3);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
+        _3 = &(*_1);                     // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
         _2 = &(*(*_3));                  // scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
         _0 = &(*_2);                     // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:5: 18:15
         StorageDead(_3);                 // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:18:14: 18:15
index 25db3b98c2562e1979071cbfed46e2e1a13d13b3..13241d882f21087cbf63a9d2ec1b8ea4e2935bfc 100644 (file)
@@ -8,7 +8,7 @@
       let mut _3: bool;                    // in scope 0 at $DIR/lower_slice_len.rs:5:8: 5:27
       let mut _4: usize;                   // in scope 0 at $DIR/lower_slice_len.rs:5:8: 5:13
       let mut _5: usize;                   // in scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
-      let mut _6: &[u8];                   // in scope 0 at $DIR/lower_slice_len.rs:5:16: 5:21
+      let mut _6: &[u8];                   // in scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
       let _7: usize;                       // in scope 0 at $DIR/lower_slice_len.rs:6:15: 6:20
       let mut _8: usize;                   // in scope 0 at $DIR/lower_slice_len.rs:6:9: 6:21
       let mut _9: bool;                    // in scope 0 at $DIR/lower_slice_len.rs:6:9: 6:21
@@ -18,8 +18,8 @@
           StorageLive(_4);                 // scope 0 at $DIR/lower_slice_len.rs:5:8: 5:13
           _4 = _1;                         // scope 0 at $DIR/lower_slice_len.rs:5:8: 5:13
           StorageLive(_5);                 // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
-          StorageLive(_6);                 // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:21
-          _6 = &(*_2);                     // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:21
+          StorageLive(_6);                 // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
+          _6 = &(*_2);                     // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
 -         _5 = core::slice::<impl [u8]>::len(move _6) -> bb1; // scope 0 at $DIR/lower_slice_len.rs:5:16: 5:27
 -                                          // mir::Constant
 -                                          // + span: $DIR/lower_slice_len.rs:5:22: 5:25
index 7bdf7b6a6482eb4d8dd5a66a8fa2ee9d18578d31..5c55ee4b9bb40e2ec75f5490776c131597e107d2 100644 (file)
@@ -4,13 +4,13 @@ fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/no-spurious-drop-after-call.rs:8:11: 8:11
     let _1: ();                          // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35
     let mut _2: std::string::String;     // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
-    let mut _3: &str;                    // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
+    let mut _3: &str;                    // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
     let _4: &str;                        // in scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
 
     bb0: {
         StorageLive(_1);                 // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35
         StorageLive(_2);                 // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
-        StorageLive(_3);                 // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
+        StorageLive(_3);                 // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
         StorageLive(_4);                 // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
         _4 = const "";                   // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
                                          // ty::Const
@@ -19,7 +19,7 @@ fn main() -> () {
                                          // mir::Constant
                                          // + span: $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
                                          // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [], len: Size { raw: 0 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 0 }) }
-        _3 = &(*_4);                     // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22
+        _3 = &(*_4);                     // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
         _2 = <str as ToString>::to_string(move _3) -> bb1; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34
                                          // mir::Constant
                                          // + span: $DIR/no-spurious-drop-after-call.rs:9:23: 9:32
index f54c8f8ab4a2e2ba60f1946669d8b1b61a05da42..f1a1f388c501a095a4e53ad71680ddf04e0f07da 100644 (file)
@@ -10,15 +10,15 @@ fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/receiver-ptr-mutability.rs:13:11: 13:11
     let _1: *mut Test as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
     let _2: ();                          // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
-    let mut _3: *const Test;             // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+    let mut _3: *const Test;             // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
     let mut _4: *mut Test;               // in scope 0 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
     let _6: &&&&*mut Test;               // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:34: 18:41
     let _7: &&&*mut Test;                // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:35: 18:41
     let _8: &&*mut Test;                 // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:36: 18:41
     let _9: &*mut Test;                  // in scope 0 at $DIR/receiver-ptr-mutability.rs:18:37: 18:41
     let _10: ();                         // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
-    let mut _11: *const Test;            // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
-    let mut _12: *mut Test;              // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
+    let mut _11: *const Test;            // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+    let mut _12: *mut Test;              // in scope 0 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
     scope 1 {
         debug ptr => _1;                 // in scope 1 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
         let _5: &&&&*mut Test as UserTypeProjection { base: UserType(2), projs: [] }; // in scope 1 at $DIR/receiver-ptr-mutability.rs:18:9: 18:16
@@ -39,10 +39,10 @@ fn main() -> () {
         FakeRead(ForLet(None), _1);      // scope 0 at $DIR/receiver-ptr-mutability.rs:14:9: 14:12
         AscribeUserType(_1, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/receiver-ptr-mutability.rs:14:14: 14:23
         StorageLive(_2);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
-        StorageLive(_3);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+        StorageLive(_3);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
         StorageLive(_4);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
         _4 = _1;                         // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
-        _3 = move _4 as *const Test (Pointer(MutToConstPointer)); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:8
+        _3 = move _4 as *const Test (Pointer(MutToConstPointer)); // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
         StorageDead(_4);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:15:7: 15:8
         _2 = Test::x(move _3) -> [return: bb2, unwind: bb4]; // scope 1 at $DIR/receiver-ptr-mutability.rs:15:5: 15:12
                                          // mir::Constant
@@ -67,10 +67,10 @@ fn main() -> () {
         AscribeUserType(_5, o, UserTypeProjection { base: UserType(3), projs: [] }); // scope 1 at $DIR/receiver-ptr-mutability.rs:18:18: 18:31
         StorageDead(_6);                 // scope 1 at $DIR/receiver-ptr-mutability.rs:18:41: 18:42
         StorageLive(_10);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
-        StorageLive(_11);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
-        StorageLive(_12);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
-        _12 = (*(*(*(*_5))));            // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
-        _11 = move _12 as *const Test (Pointer(MutToConstPointer)); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:12
+        StorageLive(_11);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+        StorageLive(_12);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+        _12 = (*(*(*(*_5))));            // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
+        _11 = move _12 as *const Test (Pointer(MutToConstPointer)); // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
         StorageDead(_12);                // scope 2 at $DIR/receiver-ptr-mutability.rs:19:11: 19:12
         _10 = Test::x(move _11) -> [return: bb3, unwind: bb4]; // scope 2 at $DIR/receiver-ptr-mutability.rs:19:5: 19:16
                                          // mir::Constant
index cdf7282c8c3c465ce43fd8c10c2dab0742cbbb63..6bb92c5e6bca0bc7437778c9cf888e32b6564f4a 100644 (file)
@@ -4,7 +4,7 @@ fn main() -> () {
     let mut _0: ();                      // return place in scope 0 at $DIR/retag.rs:29:11: 29:11
     let mut _1: i32;                     // in scope 0 at $DIR/retag.rs:30:9: 30:14
     let _2: ();                          // in scope 0 at $DIR/retag.rs:31:5: 37:6
-    let mut _4: &Test;                   // in scope 0 at $DIR/retag.rs:32:17: 32:24
+    let mut _4: &Test;                   // in scope 0 at $DIR/retag.rs:32:17: 32:36
     let _5: Test;                        // in scope 0 at $DIR/retag.rs:32:17: 32:24
     let mut _6: &mut i32;                // in scope 0 at $DIR/retag.rs:32:29: 32:35
     let mut _7: &mut i32;                // in scope 0 at $DIR/retag.rs:32:29: 32:35
@@ -15,7 +15,7 @@ fn main() -> () {
     let mut _17: &i32;                   // in scope 0 at $DIR/retag.rs:44:16: 44:18
     let _18: &i32;                       // in scope 0 at $DIR/retag.rs:44:16: 44:18
     let _19: &i32;                       // in scope 0 at $DIR/retag.rs:47:5: 47:24
-    let mut _20: &Test;                  // in scope 0 at $DIR/retag.rs:47:5: 47:12
+    let mut _20: &Test;                  // in scope 0 at $DIR/retag.rs:47:5: 47:24
     let _21: Test;                       // in scope 0 at $DIR/retag.rs:47:5: 47:12
     let mut _22: &i32;                   // in scope 0 at $DIR/retag.rs:47:21: 47:23
     let _23: &i32;                       // in scope 0 at $DIR/retag.rs:47:21: 47:23
@@ -60,11 +60,11 @@ fn main() -> () {
         _1 = const 0_i32;                // scope 0 at $DIR/retag.rs:30:17: 30:18
         StorageLive(_2);                 // scope 1 at $DIR/retag.rs:31:5: 37:6
         StorageLive(_3);                 // scope 1 at $DIR/retag.rs:32:13: 32:14
-        StorageLive(_4);                 // scope 1 at $DIR/retag.rs:32:17: 32:24
+        StorageLive(_4);                 // scope 1 at $DIR/retag.rs:32:17: 32:36
         StorageLive(_5);                 // scope 1 at $DIR/retag.rs:32:17: 32:24
         _5 = Test(const 0_i32);          // scope 1 at $DIR/retag.rs:32:17: 32:24
-        _4 = &_5;                        // scope 1 at $DIR/retag.rs:32:17: 32:24
-        Retag(_4);                       // scope 1 at $DIR/retag.rs:32:17: 32:24
+        _4 = &_5;                        // scope 1 at $DIR/retag.rs:32:17: 32:36
+        Retag(_4);                       // scope 1 at $DIR/retag.rs:32:17: 32:36
         StorageLive(_6);                 // scope 1 at $DIR/retag.rs:32:29: 32:35
         StorageLive(_7);                 // scope 1 at $DIR/retag.rs:32:29: 32:35
         _7 = &mut _1;                    // scope 1 at $DIR/retag.rs:32:29: 32:35
@@ -140,11 +140,11 @@ fn main() -> () {
         StorageDead(_16);                // scope 6 at $DIR/retag.rs:44:18: 44:19
         StorageDead(_18);                // scope 6 at $DIR/retag.rs:44:19: 44:20
         StorageLive(_19);                // scope 7 at $DIR/retag.rs:47:5: 47:24
-        StorageLive(_20);                // scope 7 at $DIR/retag.rs:47:5: 47:12
+        StorageLive(_20);                // scope 7 at $DIR/retag.rs:47:5: 47:24
         StorageLive(_21);                // scope 7 at $DIR/retag.rs:47:5: 47:12
         _21 = Test(const 0_i32);         // scope 7 at $DIR/retag.rs:47:5: 47:12
-        _20 = &_21;                      // scope 7 at $DIR/retag.rs:47:5: 47:12
-        Retag(_20);                      // scope 7 at $DIR/retag.rs:47:5: 47:12
+        _20 = &_21;                      // scope 7 at $DIR/retag.rs:47:5: 47:24
+        Retag(_20);                      // scope 7 at $DIR/retag.rs:47:5: 47:24
         StorageLive(_22);                // scope 7 at $DIR/retag.rs:47:21: 47:23
         StorageLive(_23);                // scope 7 at $DIR/retag.rs:47:21: 47:23
         _28 = const main::promoted[0];   // scope 7 at $DIR/retag.rs:47:21: 47:23
index d3862309ce46f54c6f33aa493dc522bfd709e32c..7c16f7bdaf7c10872fab83a6024325476f087af5 100644 (file)
@@ -2,11 +2,12 @@
 #![deny(warnings)]
 
 extern crate rustc_codegen_ssa;
-extern crate rustc_errors;
-extern crate rustc_middle;
 extern crate rustc_data_structures;
 extern crate rustc_driver;
+extern crate rustc_errors;
 extern crate rustc_hir;
+extern crate rustc_metadata;
+extern crate rustc_middle;
 extern crate rustc_session;
 extern crate rustc_span;
 extern crate rustc_symbol_mangling;
@@ -16,8 +17,8 @@
 use rustc_codegen_ssa::{CodegenResults, CrateInfo};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorReported;
+use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::cstore::EncodedMetadata;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::OutputFilenames;
 use rustc_session::Session;
index 9ff33e24d04d79a56480f2824d4d93e66bb58148..d576a1dd28192a15b75a337c7694afb2ffb34b09 100644 (file)
@@ -11,7 +11,7 @@ trait Sized {}
 auto trait Freeze {}
 
 #[lang = "start"]
-fn start(_main: *const u8, _argc: isize, _argv: *const *const u8) -> isize {
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
     0
 }
 
diff --git a/src/test/rustdoc-json/unions/impl.rs b/src/test/rustdoc-json/unions/impl.rs
new file mode 100644 (file)
index 0000000..0388b4a
--- /dev/null
@@ -0,0 +1,15 @@
+#![no_std]
+
+// @has impl.json "$.index[*][?(@.name=='Ux')].visibility" \"public\"
+// @has - "$.index[*][?(@.name=='Ux')].kind" \"union\"
+pub union Ux {
+    a: u32,
+    b: u64
+}
+
+// @has - "$.index[*][?(@.name=='Num')].visibility" \"public\"
+// @has - "$.index[*][?(@.name=='Num')].kind" \"trait\"
+pub trait Num {}
+
+// @count - "$.index[*][?(@.name=='Ux')].inner.impls" 1
+impl Num for Ux {}
index 679fd8997733b6696738494d0c429e381adcf204..0ec263c850e9eb80ac5726f736c230ccdf99a5a0 100644 (file)
@@ -7,7 +7,7 @@ LL |     for x in &mut xs {
    |              first mutable borrow occurs here
    |              first borrow later used here
 LL |         xs.push(1)
-   |         ^^ second mutable borrow occurs here
+   |         ^^^^^^^^^^ second mutable borrow occurs here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/asm/issue-89305.rs b/src/test/ui/asm/issue-89305.rs
new file mode 100644 (file)
index 0000000..bdcf3f3
--- /dev/null
@@ -0,0 +1,14 @@
+// Regression test for #89305, where a variable was erroneously reported
+// as both unused and possibly-uninitialized.
+
+// check-pass
+
+#![feature(asm)]
+#![warn(unused)]
+
+fn main() {
+    unsafe {
+        let x: () = asm!("nop");
+        //~^ WARNING: unused variable: `x`
+    }
+}
diff --git a/src/test/ui/asm/issue-89305.stderr b/src/test/ui/asm/issue-89305.stderr
new file mode 100644 (file)
index 0000000..9cc127b
--- /dev/null
@@ -0,0 +1,15 @@
+warning: unused variable: `x`
+  --> $DIR/issue-89305.rs:11:13
+   |
+LL |         let x: () = asm!("nop");
+   |             ^ help: if this is intentional, prefix it with an underscore: `_x`
+   |
+note: the lint level is defined here
+  --> $DIR/issue-89305.rs:7:9
+   |
+LL | #![warn(unused)]
+   |         ^^^^^^
+   = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+
+warning: 1 warning emitted
+
index d4c12b8e061cc4da070345426477856cce97d503..60cf9a533cd72cb1aa8884fdafec6e6e7ac06cfc 100644 (file)
@@ -16,12 +16,12 @@ LL |     const ID: i32 = 3;
    |     ^^^^^^^^^^^^^^^^^^
 help: disambiguate the associated constant for candidate #1
    |
-LL | const X: i32 = Foo::ID;
-   |                ~~~~~
+LL | const X: i32 = <i32 as Foo>::ID;
+   |                ~~~~~~~~~~~~~~
 help: disambiguate the associated constant for candidate #2
    |
-LL | const X: i32 = Bar::ID;
-   |                ~~~~~
+LL | const X: i32 = <i32 as Bar>::ID;
+   |                ~~~~~~~~~~~~~~
 
 error: aborting due to previous error
 
index 079989f2331af77815e92c5e36c3166b495b505a..fe9b4d630b910c9bafad6b2e205ebd1d62c23e7b 100644 (file)
@@ -9,7 +9,7 @@ LL | |     type U = str;
 LL | | }
    | |_^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`hr_associated_type_bound_2`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`hr_associated_type_bound_2`)
 note: required because of the requirements on the impl of `for<'b> X<'b>` for `u32`
   --> $DIR/hr-associated-type-bound-2.rs:11:6
    |
@@ -24,7 +24,7 @@ error[E0275]: overflow evaluating the requirement `for<'b> u32: X<'b>`
 LL |     type U = str;
    |     ^^^^^^^^^^^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`hr_associated_type_bound_2`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`hr_associated_type_bound_2`)
 note: required because of the requirements on the impl of `for<'b> X<'b>` for `u32`
   --> $DIR/hr-associated-type-bound-2.rs:11:6
    |
index f2dec87baf08b446f558e43945e66c5de6055514..2d3bb48e03be83ac3a5a042eaf9de06774d0c7c5 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
 LL | pub async fn f(x: Option<usize>) {
    |                - help: consider changing this to be mutable: `mut x`
 LL |     x.take();
-   |     ^ cannot borrow as mutable
+   |     ^^^^^^^^ cannot borrow as mutable
 
 error[E0384]: cannot assign twice to immutable variable `x`
   --> $DIR/issue-61452.rs:9:5
index 4d361c824dd6da8de946c3789dc1aa400d66646e..163053471b52daad9ce7b5394f0a693b323ba10e 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `data` as mutable, as it is not declared as mutable
 LL | async fn response(data: Vec<u8>) {
    |                   ---- help: consider changing this to be mutable: `mut data`
 LL |     data.reverse();
-   |     ^^^^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^^^ cannot borrow as mutable
 
 error: aborting due to previous error
 
index 0671cede73bbe5c16dcb1c625c4f64c206dc80be..a560334314ceaef8e9438017ad85acb6ed160f89 100644 (file)
@@ -4,7 +4,7 @@ error[E0055]: reached the recursion limit while auto-dereferencing `J`
 LL |     let x: &Bottom = &t;
    |                      ^^ deref recursion limit reached
    |
-   = help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`issue_38940`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`issue_38940`)
 
 error[E0308]: mismatched types
   --> $DIR/issue-38940.rs:43:22
index 7721f8827db8772b417f370794c94a619839f71f..2cd6d1abfdcdb2385c6e271f695aa1e3938ea3d6 100644 (file)
@@ -30,7 +30,7 @@ LL |     x
    |     - value moved here
 LL |     +
 LL |     x.clone();
-   |     ^ value borrowed here after move
+   |     ^^^^^^^^^ value borrowed here after move
    |
 help: consider further restricting this bound
    |
index 503ea49d74ee0084b7fb51c254a59740597d8f2b..ad628abcbfc2eead6bde695e9e67ab94406ff89e 100644 (file)
@@ -7,7 +7,7 @@ LL |     let y = x;
    |             ^ move out of `x` occurs here
 LL | 
 LL |     r.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable
   --> $DIR/borrow-tuple-fields.rs:18:13
@@ -17,7 +17,7 @@ LL |     let a = &x.0;
 LL |     let b = &mut x.0;
    |             ^^^^^^^^ mutable borrow occurs here
 LL |     a.use_ref();
-   |     - immutable borrow later used here
+   |     ----------- immutable borrow later used here
 
 error[E0499]: cannot borrow `x.0` as mutable more than once at a time
   --> $DIR/borrow-tuple-fields.rs:23:13
@@ -27,7 +27,7 @@ LL |     let a = &mut x.0;
 LL |     let b = &mut x.0;
    |             ^^^^^^^^ second mutable borrow occurs here
 LL |     a.use_ref();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error[E0505]: cannot move out of `x` because it is borrowed
   --> $DIR/borrow-tuple-fields.rs:28:13
@@ -37,7 +37,7 @@ LL |     let r = &x.0;
 LL |     let y = x;
    |             ^ move out of `x` occurs here
 LL |     r.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0502]: cannot borrow `x.0` as mutable because it is also borrowed as immutable
   --> $DIR/borrow-tuple-fields.rs:33:13
@@ -47,7 +47,7 @@ LL |     let a = &x.0;
 LL |     let b = &mut x.0;
    |             ^^^^^^^^ mutable borrow occurs here
 LL |     a.use_ref();
-   |     - immutable borrow later used here
+   |     ----------- immutable borrow later used here
 
 error[E0499]: cannot borrow `x.0` as mutable more than once at a time
   --> $DIR/borrow-tuple-fields.rs:38:13
@@ -57,7 +57,7 @@ LL |     let a = &mut x.0;
 LL |     let b = &mut x.0;
    |             ^^^^^^^^ second mutable borrow occurs here
 LL |     a.use_mut();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error: aborting due to 6 previous errors
 
index cf15833140927640657e04bd698e7709342a4b71..d4d646e390c31f5269ff87c814641679bb3cdbd6 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `arg` as mutable, as it is not declared as mutable
 LL | fn func(arg: S) {
    |         --- help: consider changing this to be mutable: `mut arg`
 LL |     arg.mutate();
-   |     ^^^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `arg` as mutable, as it is not declared as mutable
   --> $DIR/borrowck-argument.rs:15:9
@@ -12,7 +12,7 @@ error[E0596]: cannot borrow `arg` as mutable, as it is not declared as mutable
 LL |     fn method(&self, arg: S) {
    |                      --- help: consider changing this to be mutable: `mut arg`
 LL |         arg.mutate();
-   |         ^^^ cannot borrow as mutable
+   |         ^^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `arg` as mutable, as it is not declared as mutable
   --> $DIR/borrowck-argument.rs:21:9
@@ -20,13 +20,13 @@ error[E0596]: cannot borrow `arg` as mutable, as it is not declared as mutable
 LL |     fn default(&self, arg: S) {
    |                       --- help: consider changing this to be mutable: `mut arg`
 LL |         arg.mutate();
-   |         ^^^ cannot borrow as mutable
+   |         ^^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `arg` as mutable, as it is not declared as mutable
   --> $DIR/borrowck-argument.rs:32:17
    |
 LL |     (|arg: S| { arg.mutate() })(s);
-   |       ---       ^^^ cannot borrow as mutable
+   |       ---       ^^^^^^^^^^^^ cannot borrow as mutable
    |       |
    |       help: consider changing this to be mutable: `mut arg`
 
index 3ed76c13f6a7bf0370cd20c6888ea17e5d4fc4a5..186ecddd6d61df3ff01e052cd5349897980860c9 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
 LL |     let x = Foo { x: 3 };
    |         - help: consider changing this to be mutable: `mut x`
 LL |     x.printme();
-   |     ^ cannot borrow as mutable
+   |     ^^^^^^^^^^^ cannot borrow as mutable
 
 error: aborting due to previous error
 
index 76dc01202f6c9266f8b3acd9085fc0e80d7c1410..237071e16fc669b0f0e27414cc5ba5ccce10e115 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `*a` as mutable, as `a` is not declared as mutable
 LL |     let a: Box<_> = Box::new(A);
    |         - help: consider changing this to be mutable: `mut a`
 LL |     a.foo();
-   |     ^ cannot borrow as mutable
+   |     ^^^^^^^ cannot borrow as mutable
 
 error: aborting due to previous error
 
index fa0ae318e72cd2ce7d2cd77cfa424504602e99c7..42b6c34cd2f37484d584e53fb24ec1861701b395 100644 (file)
@@ -2,11 +2,11 @@ error[E0499]: cannot borrow `*x` as mutable more than once at a time
   --> $DIR/borrowck-borrow-mut-object-twice.rs:13:5
    |
 LL |     let y = x.f1();
-   |             - first mutable borrow occurs here
+   |             ------ first mutable borrow occurs here
 LL |     x.f2();
-   |     ^ second mutable borrow occurs here
+   |     ^^^^^^ second mutable borrow occurs here
 LL |     y.use_ref();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error: aborting due to previous error
 
index 426d5bc4726f582751f111bb306301838c7898ed..fdf6568d8397d205bacf166964b180e14f9c2d24 100644 (file)
@@ -58,7 +58,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:72:5
    |
 LL |     x.set(0, 0);
-   |     ^ cannot borrow as mutable
+   |     ^^^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
 
@@ -66,7 +66,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:76:5
    |
 LL |     x.set(0, 0);
-   |     ^ cannot borrow as mutable
+   |     ^^^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
 
@@ -74,7 +74,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:84:5
    |
 LL |     x.y_mut()
-   |     ^ cannot borrow as mutable
+   |     ^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
 
@@ -82,7 +82,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:88:5
    |
 LL |     x.y_mut()
-   |     ^ cannot borrow as mutable
+   |     ^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
 
@@ -90,7 +90,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:92:6
    |
 LL |     *x.y_mut() = 3;
-   |      ^ cannot borrow as mutable
+   |      ^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
 
@@ -98,7 +98,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:96:6
    |
 LL |     *x.y_mut() = 3;
-   |      ^ cannot borrow as mutable
+   |      ^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
 
@@ -106,7 +106,7 @@ error[E0596]: cannot borrow data in an `Rc` as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref.rs:100:6
    |
 LL |     *x.y_mut() = 3;
-   |      ^ cannot borrow as mutable
+   |      ^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<Point>`
 
index c91a4377b4c67c4b32710033dc4389be23b806f7..01379ed8512000e95bc14707de0a6918e325f532 100644 (file)
@@ -7,7 +7,7 @@ LL |     buggy_map.insert(42, &*Box::new(1));
    |                            creates a temporary which is freed while still in use
 ...
 LL |     buggy_map.insert(43, &*tmp);
-   |     --------- borrow later used here
+   |     --------------------------- borrow later used here
    |
    = note: consider using a `let` binding to create a longer lived value
 
index b8bbb31a3550f41c0f7d84740b15fb75b10df25d..0c5fd39b71871e3bfc0a48e990dc5c5ff6be551a 100644 (file)
@@ -6,7 +6,7 @@ LL |         let p = &this.x;
 LL |         &mut this.x;
    |         ^^^^^^^^^^^ mutable borrow occurs here
 LL |         p.use_ref();
-   |         - immutable borrow later used here
+   |         ----------- immutable borrow later used here
 
 error: aborting due to previous error
 
index 4b9c5a2a98ff67861b63422e1116d0dfd996a081..f909dbc4082d63c67350b98aa244c80887d009fe 100644 (file)
@@ -41,7 +41,7 @@ error[E0503]: cannot use `f.x` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:37:9
    |
 LL |         let x = f.x();
-   |                 - borrow of `f` occurs here
+   |                 ----- borrow of `f` occurs here
 LL |         f.x;
    |         ^^^ use of borrowed `f`
 LL |         drop(x);
@@ -51,7 +51,7 @@ error[E0503]: cannot use `g.0` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:44:9
    |
 LL |         let x = g.x();
-   |                 - borrow of `g` occurs here
+   |                 ----- borrow of `g` occurs here
 LL |         g.0;
    |         ^^^ use of borrowed `g`
 LL |         drop(x);
@@ -71,7 +71,7 @@ error[E0503]: cannot use `e.0` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:59:20
    |
 LL |         let x = e.x();
-   |                 - borrow of `e` occurs here
+   |                 ----- borrow of `e` occurs here
 LL |         match e {
 LL |             Baz::X(value) => value
    |                    ^^^^^ use of borrowed `e`
@@ -93,7 +93,7 @@ error[E0503]: cannot use `f.x` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:74:9
    |
 LL |         let x = f.x();
-   |                 - borrow of `*f` occurs here
+   |                 ----- borrow of `*f` occurs here
 LL |         f.x;
    |         ^^^ use of borrowed `*f`
 LL |         drop(x);
@@ -103,7 +103,7 @@ error[E0503]: cannot use `g.0` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:81:9
    |
 LL |         let x = g.x();
-   |                 - borrow of `*g` occurs here
+   |                 ----- borrow of `*g` occurs here
 LL |         g.0;
    |         ^^^ use of borrowed `*g`
 LL |         drop(x);
@@ -123,7 +123,7 @@ error[E0503]: cannot use `e.0` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:96:20
    |
 LL |         let x = e.x();
-   |                 - borrow of `*e` occurs here
+   |                 ----- borrow of `*e` occurs here
 LL |         match *e {
 LL |             Baz::X(value) => value
    |                    ^^^^^ use of borrowed `*e`
diff --git a/src/test/ui/borrowck/borrowck-for-loop-head-linkage.nll.stderr b/src/test/ui/borrowck/borrowck-for-loop-head-linkage.nll.stderr
deleted file mode 100644 (file)
index 3468f29..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-error[E0502]: cannot borrow `vector` as mutable because it is also borrowed as immutable
-  --> $DIR/borrowck-for-loop-head-linkage.rs:7:9
-   |
-LL |     for &x in &vector {
-   |               -------
-   |               |
-   |               immutable borrow occurs here
-   |               immutable borrow later used here
-LL |         let cap = vector.capacity();
-LL |         vector.extend(repeat(0));
-   |         ^^^^^^ mutable borrow occurs here
-
-error[E0502]: cannot borrow `vector` as mutable because it is also borrowed as immutable
-  --> $DIR/borrowck-for-loop-head-linkage.rs:8:9
-   |
-LL |     for &x in &vector {
-   |               -------
-   |               |
-   |               immutable borrow occurs here
-   |               immutable borrow later used here
-...
-LL |         vector[1] = 5;
-   |         ^^^^^^ mutable borrow occurs here
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0502`.
index a1ac45795fae2e4206d2adba477bc50e81b78d20..99d08e905d5fd0ab20451e0b4c2319043d1f29a7 100644 (file)
@@ -16,15 +16,17 @@ LL | |         })
 error[E0500]: closure requires unique access to `f` but it is already borrowed
   --> $DIR/borrowck-insert-during-each.rs:18:9
    |
-LL |     f.foo(
-   |     - --- first borrow later used by call
-   |     |
-   |     borrow occurs here
-LL |
-LL |         |a| {
-   |         ^^^ closure construction occurs here
-LL |             f.n.insert(*a);
-   |             --- second borrow occurs due to use of `f` in closure
+LL |       f.foo(
+   |       - --- first borrow later used by call
+   |  _____|
+   | |
+LL | |
+LL | |         |a| {
+   | |         ^^^ closure construction occurs here
+LL | |             f.n.insert(*a);
+   | |             --- second borrow occurs due to use of `f` in closure
+LL | |         })
+   | |__________- borrow occurs here
 
 error: aborting due to 2 previous errors
 
index 4ea4eb8f007591570b47b50e19289216795af7e1..390bb9384f8aec20319699c0ec080c23ac685de6 100644 (file)
@@ -6,7 +6,7 @@ LL |       Some(ref _y) => {
 LL |         let _a = x;
    |                  ^ move out of `x` occurs here
 LL |         _y.use_ref();
-   |         -- borrow later used here
+   |         ------------ borrow later used here
 
 error: aborting due to previous error
 
index 68a82bdb57c5510d31e504d213a6fb94d26d4183..e47efc0e0b34677629ce833022bd00ef99153e9d 100644 (file)
@@ -7,7 +7,7 @@ LL |     }
 LL |     borrow_mut(&mut *v);
    |                ^^^^^^^ mutable borrow occurs here
 LL |     _w.use_ref();
-   |     -- immutable borrow later used here
+   |     ------------ immutable borrow later used here
 
 error: aborting due to previous error
 
index 07b11b3e7282824d0a138b6d087b597734cc54f6..40c14f54cb88157039f4c5d6f3248c0a8a10e96d 100644 (file)
@@ -6,7 +6,7 @@ LL |     let _w = &v;
 LL |     borrow_mut(&mut *v);
    |                ^^^^^^^ mutable borrow occurs here
 LL |     _w.use_ref();
-   |     -- immutable borrow later used here
+   |     ------------ immutable borrow later used here
 
 error: aborting due to previous error
 
index ac25502ad053c052f96705f5b5223ce170da10e3..3548da35b613926a4acf0ef7ebf0585127f2810b 100644 (file)
@@ -10,7 +10,7 @@ LL |         println!("v={}", *v);
    |                          -- move occurs due to use in closure
 LL |     });
 LL |     w.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0505]: cannot move out of `v` because it is borrowed
   --> $DIR/borrowck-loan-blocks-move-cc.rs:24:19
@@ -24,7 +24,7 @@ LL |         println!("v={}", *v);
    |                          -- move occurs due to use in closure
 LL |     });
 LL |     w.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error: aborting due to 2 previous errors
 
index 615660febbce2e9b0914114de48ad134ed016201..b5c6b101f765caffdb6000162d4ea6cf76b05a2c 100644 (file)
@@ -6,7 +6,7 @@ LL |     let w = &v;
 LL |     take(v);
    |          ^ move out of `v` occurs here
 LL |     w.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error: aborting due to previous error
 
index cd288065b74f7df96b14e5c6bb09e037bdb7f3b0..0dd720ff6ce0424b630ce22ca61e2268bba89ca6 100644 (file)
@@ -4,7 +4,7 @@ error[E0382]: borrow of moved value: `x`
 LL |     let x = Foo(Box::new(3));
    |         - move occurs because `x` has type `Foo`, which does not implement the `Copy` trait
 LL |     let _y = {x} + x.clone(); // the `{x}` forces a move to occur
-   |               -    ^ value borrowed here after move
+   |               -    ^^^^^^^^^ value borrowed here after move
    |               |
    |               value moved here
 
index aa874c34a22ed5d5320658833143273b3c2860a4..1d8d04c9181c2f62c82f16f94f00644cdb07c8a0 100644 (file)
@@ -17,7 +17,7 @@ LL |     let q = &mut p;
    |             ------ mutable borrow occurs here
 ...
 LL |     p.times(3);
-   |     ^ immutable borrow occurs here
+   |     ^^^^^^^^^^ immutable borrow occurs here
 LL | 
 LL |     *q + 3; // OK to use the new alias `q`
    |     -- mutable borrow later used here
index 489ec7d04ed1d1791e227932db4b760e029e100c..74cad575d276a7fdadcf06b3af022e7564c5a68b 100644 (file)
@@ -1,13 +1,15 @@
 error[E0502]: cannot borrow `p` as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-loan-rcvr.rs:23:14
    |
-LL |     p.blockm(|| {
-   |     - ------ ^^ mutable borrow occurs here
-   |     | |
-   |     | immutable borrow later used by call
-   |     immutable borrow occurs here
-LL |         p.x = 10;
-   |         --- second borrow occurs due to use of `p` in closure
+LL |       p.blockm(|| {
+   |       - ------ ^^ mutable borrow occurs here
+   |       | |
+   |  _____| immutable borrow later used by call
+   | |
+LL | |         p.x = 10;
+   | |         --- second borrow occurs due to use of `p` in closure
+LL | |     })
+   | |______- immutable borrow occurs here
 
 error[E0502]: cannot borrow `p` as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-loan-rcvr.rs:34:5
@@ -15,7 +17,7 @@ error[E0502]: cannot borrow `p` as immutable because it is also borrowed as muta
 LL |     let l = &mut p;
    |             ------ mutable borrow occurs here
 LL |     p.impurem();
-   |     ^ immutable borrow occurs here
+   |     ^^^^^^^^^^^ immutable borrow occurs here
 LL | 
 LL |     l.x += 1;
    |     -------- mutable borrow later used here
index e4840fba67299999f288548d5e0188e974e7efe4..b305e3c0a163a27bc10a94d50dc2e4b3fdd57276 100644 (file)
@@ -7,7 +7,7 @@ LL |
 LL |     let z = *a;
    |             ^^ move out of `*a` occurs here
 LL |     b.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error: aborting due to previous error
 
index 77f5b72e51c5dda8f90fad7d62537694f25052e5..d5ff0c501c4bd00f0a3e7c96c16f7ab9cce8d651 100644 (file)
@@ -7,7 +7,7 @@ LL |     let t1 = t0;
    |              ^^ move out of `t0` occurs here
 LL |     *t1 = 22;
 LL |     p.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error: aborting due to previous error
 
index 0a29d2bb1d54ec462a5ff0b3f103f72f48fac000..79745065070977f884d6f4f778251d40acdfea66 100644 (file)
@@ -2,7 +2,7 @@ error[E0507]: cannot move out of an `Rc`
   --> $DIR/borrowck-move-out-of-overloaded-auto-deref.rs:4:14
    |
 LL |     let _x = Rc::new(vec![1, 2]).into_iter();
-   |              ^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Vec<i32>`, which does not implement the `Copy` trait
 
 error: aborting due to previous error
 
index a4090777939029b5b725667642285ed116a04e48..15ac737606d66ce03e8bae889d2126c8b454b21d 100644 (file)
@@ -5,8 +5,9 @@ LL |             1 => { addr.push(&mut x); }
    |                              ^^^^^^ second mutable borrow occurs here
 LL |             2 => { addr.push(&mut x); }
 LL |             _ => { addr.push(&mut x); }
-   |                    ----      ------ first mutable borrow occurs here
-   |                    |
+   |                    -----------------
+   |                    |         |
+   |                    |         first mutable borrow occurs here
    |                    first borrow later used here
 
 error[E0499]: cannot borrow `x` as mutable more than once at a time
@@ -15,8 +16,9 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time
 LL |             2 => { addr.push(&mut x); }
    |                              ^^^^^^ second mutable borrow occurs here
 LL |             _ => { addr.push(&mut x); }
-   |                    ----      ------ first mutable borrow occurs here
-   |                    |
+   |                    -----------------
+   |                    |         |
+   |                    |         first mutable borrow occurs here
    |                    first borrow later used here
 
 error[E0499]: cannot borrow `x` as mutable more than once at a time
index f2baee09376e24c77c16faec640c5f9977ef09df..ef811b849052a769eebd1f8c1906714055215e4a 100644 (file)
@@ -7,7 +7,7 @@ LL |     let mut t2 = &mut t0;
    |                  ^^^^^^^ mutable borrow occurs here
 LL |     **t2 += 1;              // Mutates `*t0`
 LL |     p.use_ref();
-   |     - immutable borrow later used here
+   |     ----------- immutable borrow later used here
 
 error[E0499]: cannot borrow `t0` as mutable more than once at a time
   --> $DIR/borrowck-mut-borrow-of-mut-base-ptr.rs:19:18
@@ -18,7 +18,7 @@ LL |     let mut t2 = &mut t0;
    |                  ^^^^^^^ second mutable borrow occurs here
 LL |     **t2 += 1;                  // Mutates `*t0` but not through `*p`
 LL |     p.use_mut();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/borrowck/borrowck-object-lifetime.nll.stderr b/src/test/ui/borrowck/borrowck-object-lifetime.nll.stderr
deleted file mode 100644 (file)
index 49c3f86..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable
-  --> $DIR/borrowck-object-lifetime.rs:20:13
-   |
-LL |     let y = x.borrowed();
-   |             - immutable borrow occurs here
-LL |     let z = x.mut_borrowed();
-   |             ^ mutable borrow occurs here
-LL |     y.use_ref();
-   |     - immutable borrow later used here
-
-error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
-  --> $DIR/borrowck-object-lifetime.rs:26:13
-   |
-LL |     let y = x.borrowed();
-   |             - immutable borrow occurs here
-LL |     let z = &mut x;
-   |             ^^^^^^ mutable borrow occurs here
-LL |     y.use_ref();
-   |     - immutable borrow later used here
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0502`.
index cf94c74dec222a9e7f2087b07275d2a92e1f940e..215ed760ae1ebc799376f3ecf87c7c057bb95481 100644 (file)
@@ -2,21 +2,21 @@ error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immut
   --> $DIR/borrowck-object-lifetime.rs:20:13
    |
 LL |     let y = x.borrowed();
-   |             - immutable borrow occurs here
+   |             ------------ immutable borrow occurs here
 LL |     let z = x.mut_borrowed();
    |             ^^^^^^^^^^^^^^^^ mutable borrow occurs here
 LL |     y.use_ref();
-   |     - immutable borrow later used here
+   |     ----------- immutable borrow later used here
 
 error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-object-lifetime.rs:26:13
    |
 LL |     let y = x.borrowed();
-   |             - immutable borrow occurs here
+   |             ------------ immutable borrow occurs here
 LL |     let z = &mut x;
    |             ^^^^^^ mutable borrow occurs here
 LL |     y.use_ref();
-   |     - immutable borrow later used here
+   |     ----------- immutable borrow later used here
 
 error: aborting due to 2 previous errors
 
index 978e1291722a3bd3518b135c67ca7525f0e50928..087f2ac799eebc3eff5668846b48e09d53af8b74 100644 (file)
@@ -6,7 +6,7 @@ LL |     let p = &mut f[&s];
 LL |     let q = &f[&s];
    |              ^ immutable borrow occurs here
 LL |     p.use_mut();
-   |     - mutable borrow later used here
+   |     ----------- mutable borrow later used here
 
 error[E0499]: cannot borrow `*f` as mutable more than once at a time
   --> $DIR/borrowck-overloaded-index-autoderef.rs:43:18
@@ -16,7 +16,7 @@ LL |     let p = &mut f[&s];
 LL |     let q = &mut f[&s];
    |                  ^ second mutable borrow occurs here
 LL |     p.use_mut();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error[E0499]: cannot borrow `f.foo` as mutable more than once at a time
   --> $DIR/borrowck-overloaded-index-autoderef.rs:53:18
@@ -26,7 +26,7 @@ LL |     let p = &mut f.foo[&s];
 LL |     let q = &mut f.foo[&s];
    |                  ^^^^^ second mutable borrow occurs here
 LL |     p.use_mut();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error[E0502]: cannot borrow `f.foo` as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-overloaded-index-autoderef.rs:65:18
@@ -36,7 +36,7 @@ LL |     let p = &f.foo[&s];
 LL |     let q = &mut f.foo[&s];
    |                  ^^^^^ mutable borrow occurs here
 LL |     p.use_ref();
-   |     - immutable borrow later used here
+   |     ----------- immutable borrow later used here
 
 error[E0506]: cannot assign to `f.foo` because it is borrowed
   --> $DIR/borrowck-overloaded-index-autoderef.rs:71:5
@@ -46,7 +46,7 @@ LL |     let p = &f.foo[&s];
 LL |     f.foo = g;
    |     ^^^^^^^^^ assignment to borrowed `f.foo` occurs here
 LL |     p.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0506]: cannot assign to `*f` because it is borrowed
   --> $DIR/borrowck-overloaded-index-autoderef.rs:77:5
@@ -56,7 +56,7 @@ LL |     let p = &f.foo[&s];
 LL |     *f = g;
    |     ^^^^^^ assignment to borrowed `*f` occurs here
 LL |     p.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0506]: cannot assign to `f.foo` because it is borrowed
   --> $DIR/borrowck-overloaded-index-autoderef.rs:83:5
@@ -66,7 +66,7 @@ LL |     let p = &mut f.foo[&s];
 LL |     f.foo = g;
    |     ^^^^^^^^^ assignment to borrowed `f.foo` occurs here
 LL |     p.use_mut();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0506]: cannot assign to `*f` because it is borrowed
   --> $DIR/borrowck-overloaded-index-autoderef.rs:89:5
@@ -76,7 +76,7 @@ LL |     let p = &mut f.foo[&s];
 LL |     *f = g;
    |     ^^^^^^ assignment to borrowed `*f` occurs here
 LL |     p.use_mut();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error: aborting due to 8 previous errors
 
index db73d4c04acc808b10ee4bc9e7a29ea931371ffa..d05996413dd41a5845c58bd7c20b6f7f0c0f7706 100644 (file)
@@ -8,7 +8,7 @@ LL |     let z = &x;
    |             ^^ immutable borrow occurs here
 ...
 LL |     y.use_mut();
-   |     - mutable borrow later used here
+   |     ----------- mutable borrow later used here
 
 error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
   --> $DIR/borrowck-report-with-custom-diagnostic.rs:21:21
@@ -20,7 +20,7 @@ LL |             let z = &mut x;
    |                     ^^^^^^ mutable borrow occurs here
 ...
 LL |             y.use_ref();
-   |             - immutable borrow later used here
+   |             ----------- immutable borrow later used here
 
 error[E0499]: cannot borrow `x` as mutable more than once at a time
   --> $DIR/borrowck-report-with-custom-diagnostic.rs:36:17
@@ -32,7 +32,7 @@ LL |         let z = &mut x;
    |                 ^^^^^^ second mutable borrow occurs here
 ...
 LL |         y.use_mut();
-   |         - first borrow later used here
+   |         ----------- first borrow later used here
 
 error: aborting due to 3 previous errors
 
index 1c55953c91fb799dc525ef46d5cd62a8a450b292..b39215b9aab5fca8c2c01f7f7175b0eb75f04b00 100644 (file)
@@ -7,7 +7,7 @@ LL |     swap(&mut t0, &mut t1);
    |          ^^^^^^^ mutable borrow occurs here
 LL |     *t1 = 22;
 LL |     p.use_ref();
-   |     - immutable borrow later used here
+   |     ----------- immutable borrow later used here
 
 error: aborting due to previous error
 
index 61569b9cac10637a719deac6483eb73d9fc1c628..4bd7d54cffedacef1094d6e49cac88aeb5763c9b 100644 (file)
@@ -6,7 +6,7 @@ LL |             let ra = &mut u.s.a;
 LL |             let b = u.c;
    |                     ^^^ use of borrowed `u.s.a`
 LL |             ra.use_mut();
-   |             -- borrow later used here
+   |             ------------ borrow later used here
 
 error: aborting due to previous error
 
index 923edc8edae7d013688efd985224cdf280942df4..6dbe4c74b58428989bc6f6f3242e22e3b0ac36d8 100644 (file)
@@ -6,7 +6,7 @@ LL |     let w = &mut v;
 LL |     borrow(&*v);
    |            ^^^ immutable borrow occurs here
 LL |     w.use_mut();
-   |     - mutable borrow later used here
+   |     ----------- mutable borrow later used here
 
 error[E0502]: cannot borrow `*v` as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-uniq-via-lend.rs:53:12
@@ -16,7 +16,7 @@ LL |     x = &mut v;
 LL |     borrow(&*v);
    |            ^^^ immutable borrow occurs here
 LL |     x.use_mut();
-   |     - mutable borrow later used here
+   |     ----------- mutable borrow later used here
 
 error: aborting due to 2 previous errors
 
index 5141fcc1bb261f4a454a406506c2f117c571adaf..eb0f24b9b7a50016b36db8691c3b7b9a34dedd03 100644 (file)
@@ -5,8 +5,9 @@ LL |     let vb: &mut [isize] = &mut v;
    |                            ------ first mutable borrow occurs here
 ...
 LL |             v.push(tail[0] + tail[1]);
-   |             ^      ------- first borrow later used here
-   |             |
+   |             ^^^^^^^-------^^^^^^^^^^^
+   |             |      |
+   |             |      first borrow later used here
    |             second mutable borrow occurs here
 
 error: aborting due to previous error
index 41c9b3be28164d2dbbce6736e7b073b42a113882..ddd89afe5bf917c35e5f84c3e6f2782185ee4e33 100644 (file)
@@ -8,7 +8,7 @@ LL |             vec[0] = Box::new(4);
    |             ^^^^^^ assignment to borrowed `vec[_]` occurs here
 LL |
 LL |             _a.use_ref();
-   |             -- borrow later used here
+   |             ------------ borrow later used here
 
 error[E0506]: cannot assign to `vec[_]` because it is borrowed
   --> $DIR/borrowck-vec-pattern-nesting.rs:23:13
@@ -20,7 +20,7 @@ LL |             vec[0] = Box::new(4);
    |             ^^^^^^ assignment to borrowed `vec[_]` occurs here
 LL |
 LL |             _b.use_ref();
-   |             -- borrow later used here
+   |             ------------ borrow later used here
 
 error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice
   --> $DIR/borrowck-vec-pattern-nesting.rs:34:11
index 89391f4099a21e5f626869042875feccbfbd93f9..69dca7e7b565cbe3ae5ae0f5e781bc373c60b20b 100644 (file)
@@ -2,7 +2,7 @@ error[E0596]: cannot borrow data in a `&` reference as mutable
   --> $DIR/index-mut-help-with-impl.rs:9:5
    |
 LL |     Index::index(&v, 1..2).make_ascii_uppercase();
-   |     ^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
 
 error: aborting due to previous error
 
index 52b9ad496e5f851084f9085ae030c9364e9d7edc..057c6ee15f36c035988fa541ade879b684a775f7 100644 (file)
@@ -2,7 +2,7 @@ error[E0596]: cannot borrow data in an index of `HashMap<&str, String>` as mutab
   --> $DIR/index-mut-help.rs:11:5
    |
 LL |     map["peter"].clear();
-   |     ^^^^^^^^^^^^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<&str, String>`
 
index 5cffa1b51219ff37fba6adbaf4d666f174c743d9..29b4c8c38d7696f6ced9244bca8630ef148b1d58 100644 (file)
@@ -2,7 +2,7 @@ error[E0596]: cannot borrow `*TAB[_]` as mutable, as `TAB` is an immutable stati
   --> $DIR/issue-42344.rs:4:5
    |
 LL |     TAB[0].iter_mut();
-   |     ^^^^^^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^^^^^^ cannot borrow as mutable
 
 error: aborting due to previous error
 
index f8a9608ad373a5a13e764659bd88ea12567481e1..ef1a16ea953e26cea9392b269e3f50be4ef5b513 100644 (file)
@@ -4,7 +4,7 @@ error[E0499]: cannot borrow `*bar` as mutable more than once at a time
 LL |         Some(baz) => {
    |              --- first mutable borrow occurs here
 LL |             bar.take();
-   |             ^^^ second mutable borrow occurs here
+   |             ^^^^^^^^^^ second mutable borrow occurs here
 LL |             drop(baz);
    |                  --- first borrow later used here
 
index 891f70ed7f6b46b8a987f57897a3b3c9d3efd9ea..27123ef2be1ef91355c9f789a0aad0fd75cc8f08 100644 (file)
@@ -2,7 +2,7 @@ error[E0506]: cannot assign to `self.container_field` because it is borrowed
   --> $DIR/issue-81365-10.rs:21:9
    |
 LL |         let first = &self.deref().target_field;
-   |                      ---- borrow of `self.container_field` occurs here
+   |                      ------------ borrow of `self.container_field` occurs here
 LL |         self.container_field = true;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
 LL |         first;
index 7c0e9f43bd0b087eda7e3cc52ac3910a81766ffc..8201894c6db4cae6182b1791b9e00e90d2a9538a 100644 (file)
@@ -2,7 +2,7 @@ error[E0506]: cannot assign to `self.container_field` because it is borrowed
   --> $DIR/issue-81365-5.rs:28:9
    |
 LL |         let first = self.get();
-   |                     ---- borrow of `self.container_field` occurs here
+   |                     ---------- borrow of `self.container_field` occurs here
 LL |         self.container_field = true;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
 LL |         first;
index f272477a9f5b393dfb7040adc56c993b3ede5ed2..25f343117a3715a7a29ea5b3bcf188e603bceafc 100644 (file)
@@ -7,7 +7,7 @@ LL |         for v in self.0.values() {
    |                  |      help: use mutable method: `values_mut()`
    |                  this iterator yields `&` references
 LL |             v.flush();
-   |             ^ `v` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |             ^^^^^^^^^ `v` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/borrowck/issue-82462.nll.stderr b/src/test/ui/borrowck/issue-82462.nll.stderr
deleted file mode 100644 (file)
index 10497c3..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
-  --> $DIR/issue-82462.rs:18:9
-   |
-LL |     for x in DroppingSlice(&*v).iter() {
-   |              ------------------
-   |              |               |
-   |              |               immutable borrow occurs here
-   |              a temporary with access to the immutable borrow is created here ...
-LL |         v.push(*x);
-   |         ^ mutable borrow occurs here
-LL |         break;
-LL |     }
-   |     - ... and the immutable borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DroppingSlice`
-   |
-help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
-   |
-LL |     };
-   |      +
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0502`.
index 29c0429f2a046bf7b69d0bf2752f4f633b256b61..59ca4867fd7474f0d170dff436a69d306337dfdf 100644 (file)
@@ -7,7 +7,7 @@ LL |     match heap.peek_mut() {
    |           first mutable borrow occurs here
    |           a temporary with access to the first borrow is created here ...
 LL |         Some(_) => { heap.pop(); },
-   |                      ^^^^ second mutable borrow occurs here
+   |                      ^^^^^^^^^^ second mutable borrow occurs here
 ...
 LL | }
    | - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option<PeekMut<'_, i32>>`
index af83c6ea6d903a9e259f0457d9b80586f63f6f5f..80acaa7d21c52dcb3c18fe8de398b70db7009fbc 100644 (file)
@@ -5,7 +5,7 @@ LL |     let rofl: &Vec<Vec<i32>> = &mut test;
    |         ---- help: consider changing this to be a mutable reference: `&mut Vec<Vec<i32>>`
 LL |
 LL |     rofl.push(Vec::new());
-   |     ^^^^ `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^ `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0594]: cannot assign to `*r`, which is behind a `&` reference
   --> $DIR/issue-85765.rs:12:5
index e4c51bb77c9edca9b779010c70a92fda2aa7b675..2ffe7ff64133db13f925b716e398ebfb60fe3a04 100644 (file)
@@ -53,7 +53,7 @@ error[E0596]: cannot borrow `f` as mutable, as it is not declared as mutable
   --> $DIR/mut-borrow-of-mut-ref.rs:35:5
    |
 LL |     f.bar();
-   |     ^ cannot borrow as mutable
+   |     ^^^^^^^ cannot borrow as mutable
    |
 help: consider making the binding mutable
    |
index 4fcb693f1bf1df7bcf9b3865c3be2c1641b288a4..e6895b27f35d800b4114f9a03dbc904f3c4c3370 100644 (file)
@@ -6,7 +6,7 @@ LL |     let first = &mut void;
 LL |     let second = &mut void;
    |                  ^^^^^^^^^ second mutable borrow occurs here
 LL |     first.use_mut();
-   |     ----- first borrow later used here
+   |     --------------- first borrow later used here
 
 error[E0499]: cannot borrow `inner_void` as mutable more than once at a time
   --> $DIR/mut-borrow-outside-loop.rs:15:28
@@ -17,7 +17,7 @@ LL |         let inner_second = &mut inner_void;
    |                            ^^^^^^^^^^^^^^^ second mutable borrow occurs here
 LL |         inner_second.use_mut();
 LL |         inner_first.use_mut();
-   |         ----------- first borrow later used here
+   |         --------------------- first borrow later used here
 
 error: aborting due to 2 previous errors
 
index d4e515d12bbb5d563898fbbe6b5781ca77a574d7..95896c6bbf98756dbc3801bd0735ccd2bcea123e 100644 (file)
@@ -2,7 +2,7 @@ error[E0499]: cannot borrow `foo` as mutable more than once at a time
   --> $DIR/two-phase-across-loop.rs:17:22
    |
 LL |         strings.push(foo.get_string());
-   |                      ^^^ `foo` was mutably borrowed here in the previous iteration of the loop
+   |                      ^^^^^^^^^^^^^^^^ `foo` was mutably borrowed here in the previous iteration of the loop
 
 error: aborting due to previous error
 
index 9bfd8b994bf23b18f3bff6bc670c0717f92667f0..a89bb941532b62cd54a8477cc5382336a60523d3 100644 (file)
@@ -1,13 +1,18 @@
 error[E0502]: cannot borrow `vec` as mutable because it is also borrowed as immutable
   --> $DIR/two-phase-cannot-nest-mut-self-calls.rs:16:9
    |
-LL |     vec.get({
-   |     --- --- immutable borrow later used by call
-   |     |
-   |     immutable borrow occurs here
-LL | 
-LL |         vec.push(2);
-   |         ^^^ mutable borrow occurs here
+LL |       vec.get({
+   |       -   --- immutable borrow later used by call
+   |  _____|
+   | |
+LL | |
+LL | |         vec.push(2);
+   | |         ^^^^^^^^^^^ mutable borrow occurs here
+LL | |
+LL | |
+LL | |         0
+LL | |     });
+   | |______- immutable borrow occurs here
 
 error: aborting due to previous error
 
index 33fa4a3a15075ab5fc066359c21e5fe975634991..2e53e17a31bb082ef9d8403dd3bf49dca7703051 100644 (file)
@@ -12,8 +12,9 @@ error[E0499]: cannot borrow `foo` as mutable more than once at a time
   --> $DIR/two-phase-multi-mut.rs:11:16
    |
 LL |     foo.method(&mut foo);
-   |     --- ------ ^^^^^^^^ second mutable borrow occurs here
-   |     |   |
+   |     -----------^^^^^^^^-
+   |     |   |      |
+   |     |   |      second mutable borrow occurs here
    |     |   first borrow later used by call
    |     first mutable borrow occurs here
 
index e4fceb197be59af21471a464c42b06f6a1b8bc68..6cff53399ca7f12ebb08fa0dba026bc2b3e54fac 100644 (file)
@@ -27,8 +27,9 @@ LL |     let shared = &v;
    |                  -- immutable borrow occurs here
 LL | 
 LL |     v.push(shared.len());
-   |     ^      ------ immutable borrow later used here
-   |     |
+   |     ^^^^^^^------------^
+   |     |      |
+   |     |      immutable borrow later used here
    |     mutable borrow occurs here
    |
    = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
index e4fceb197be59af21471a464c42b06f6a1b8bc68..6cff53399ca7f12ebb08fa0dba026bc2b3e54fac 100644 (file)
@@ -27,8 +27,9 @@ LL |     let shared = &v;
    |                  -- immutable borrow occurs here
 LL | 
 LL |     v.push(shared.len());
-   |     ^      ------ immutable borrow later used here
-   |     |
+   |     ^^^^^^^------------^
+   |     |      |
+   |     |      immutable borrow later used here
    |     mutable borrow occurs here
    |
    = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
index 52017394e898e901a5e7602ba02874ef1ea828c7..0ae6fe78c6ae1c928f82621e5aa77b12108b7360 100644 (file)
@@ -5,8 +5,9 @@ LL |     let shared = &v;
    |                  -- immutable borrow occurs here
 LL | 
 LL |     v.extend(shared);
-   |     ^        ------ immutable borrow later used here
-   |     |
+   |     ^^^^^^^^^------^
+   |     |        |
+   |     |        immutable borrow later used here
    |     mutable borrow occurs here
 
 error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
@@ -26,8 +27,9 @@ LL |     let shared = &v;
    |                  -- immutable borrow occurs here
 LL | 
 LL |     v.push(shared.len());
-   |     ^      ------ immutable borrow later used here
-   |     |
+   |     ^^^^^^^------------^
+   |     |      |
+   |     |      immutable borrow later used here
    |     mutable borrow occurs here
 
 error: aborting due to 3 previous errors
index 52017394e898e901a5e7602ba02874ef1ea828c7..0ae6fe78c6ae1c928f82621e5aa77b12108b7360 100644 (file)
@@ -5,8 +5,9 @@ LL |     let shared = &v;
    |                  -- immutable borrow occurs here
 LL | 
 LL |     v.extend(shared);
-   |     ^        ------ immutable borrow later used here
-   |     |
+   |     ^^^^^^^^^------^
+   |     |        |
+   |     |        immutable borrow later used here
    |     mutable borrow occurs here
 
 error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
@@ -26,8 +27,9 @@ LL |     let shared = &v;
    |                  -- immutable borrow occurs here
 LL | 
 LL |     v.push(shared.len());
-   |     ^      ------ immutable borrow later used here
-   |     |
+   |     ^^^^^^^------------^
+   |     |      |
+   |     |      immutable borrow later used here
    |     mutable borrow occurs here
 
 error: aborting due to 3 previous errors
index d2ea5ab2077e577503f59f91e5adcf348d350dbb..52e8de3c4ac7d452c127aa8d11fc2623898544fa 100644 (file)
@@ -5,8 +5,9 @@ LL |         let shared = &v;
    |                      -- immutable borrow occurs here
 LL | 
 LL |         v.push(shared.len());
-   |         ^      ------ immutable borrow later used here
-   |         |
+   |         ^^^^^^^------------^
+   |         |      |
+   |         |      immutable borrow later used here
    |         mutable borrow occurs here
 
 error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
@@ -16,8 +17,9 @@ LL |         let shared = &v;
    |                      -- immutable borrow occurs here
 LL | 
 LL |         v.push(shared.len());
-   |         ^      ------ immutable borrow later used here
-   |         |
+   |         ^^^^^^^------------^
+   |         |      |
+   |         |      immutable borrow later used here
    |         mutable borrow occurs here
 
 error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
@@ -27,8 +29,9 @@ LL |         let shared = &v;
    |                      -- immutable borrow occurs here
 LL | 
 LL |         v.push(shared.len());
-   |         ^      ------ immutable borrow later used here
-   |         |
+   |         ^^^^^^^------------^
+   |         |      |
+   |         |      immutable borrow later used here
    |         mutable borrow occurs here
 
 error: aborting due to 3 previous errors
index 03f49d2d92fa14cfc2e21912db9d8bf391aab86f..aab21c9e78beff585061e473901961a8f3e5aa30 100644 (file)
@@ -5,8 +5,9 @@ LL |         let shared = &v;
    |                      -- immutable borrow occurs here
 LL | 
 LL |         v.push(shared.len());
-   |         ^      ------ immutable borrow later used here
-   |         |
+   |         ^^^^^^^------------^
+   |         |      |
+   |         |      immutable borrow later used here
    |         mutable borrow occurs here
    |
 note: the lint level is defined here
@@ -24,8 +25,9 @@ LL |         let shared = &v;
    |                      -- immutable borrow occurs here
 LL | 
 LL |         v.push(shared.len());
-   |         ^      ------ immutable borrow later used here
-   |         |
+   |         ^^^^^^^------------^
+   |         |      |
+   |         |      immutable borrow later used here
    |         mutable borrow occurs here
    |
 note: the lint level is defined here
index c66f3cbed918d16b1e114e95358b1430b08db0d8..cffbf0706feada511cd11f69949f0280434ebfed 100644 (file)
@@ -7,7 +7,7 @@ LL |     v[0].push_str({
    |     first mutable borrow occurs here
 LL | 
 LL |         v.push(format!("foo"));
-   |         ^ second mutable borrow occurs here
+   |         ^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
 
 error: aborting due to previous error
 
index 7d0e15667505d15581e1609d50b5aa9116cbe64f..5a240d90011e4ea64d9232947dd260f1a647ad6b 100644 (file)
@@ -13,7 +13,7 @@ error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as im
   --> $DIR/two-phase-surprise-no-conflict.rs:57:17
    |
 LL |                 self.hash_expr(&self.cx_mut.body(eid).value);
-   |                 ^^^^^---------^^-----------^^^^^^^^^^^^^^^^^
+   |                 ^^^^^---------^^---------------------^^^^^^^
    |                 |    |          |
    |                 |    |          immutable borrow occurs here
    |                 |    immutable borrow later used by call
@@ -23,8 +23,9 @@ error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time
   --> $DIR/two-phase-surprise-no-conflict.rs:119:51
    |
 LL |     reg.register_static(Box::new(TrivialPass::new(&mut reg.sess_mut)));
-   |     --- ---------------                           ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
-   |     |   |
+   |     ----------------------------------------------^^^^^^^^^^^^^^^^^---
+   |     |   |                                         |
+   |     |   |                                         second mutable borrow occurs here
    |     |   first borrow later used by call
    |     first mutable borrow occurs here
 
@@ -32,8 +33,9 @@ error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time
   --> $DIR/two-phase-surprise-no-conflict.rs:122:54
    |
 LL |     reg.register_bound(Box::new(TrivialPass::new_mut(&mut reg.sess_mut)));
-   |     --- --------------                               ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
-   |     |   |
+   |     -------------------------------------------------^^^^^^^^^^^^^^^^^---
+   |     |   |                                            |
+   |     |   |                                            second mutable borrow occurs here
    |     |   first borrow later used by call
    |     first mutable borrow occurs here
 
@@ -41,8 +43,9 @@ error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time
   --> $DIR/two-phase-surprise-no-conflict.rs:125:53
    |
 LL |     reg.register_univ(Box::new(TrivialPass::new_mut(&mut reg.sess_mut)));
-   |     --- -------------                               ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
-   |     |   |
+   |     ------------------------------------------------^^^^^^^^^^^^^^^^^---
+   |     |   |                                           |
+   |     |   |                                           second mutable borrow occurs here
    |     |   first borrow later used by call
    |     first mutable borrow occurs here
 
@@ -50,8 +53,9 @@ error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time
   --> $DIR/two-phase-surprise-no-conflict.rs:128:44
    |
 LL |     reg.register_ref(&TrivialPass::new_mut(&mut reg.sess_mut));
-   |     --- ------------                       ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
-   |     |   |
+   |     ---------------------------------------^^^^^^^^^^^^^^^^^--
+   |     |   |                                  |
+   |     |   |                                  second mutable borrow occurs here
    |     |   first borrow later used by call
    |     first mutable borrow occurs here
 
@@ -102,8 +106,9 @@ error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time
   --> $DIR/two-phase-surprise-no-conflict.rs:154:54
    |
 LL |     reg.register_bound(Box::new(CapturePass::new_mut(&mut reg.sess_mut)));
-   |     --- --------------                               ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
-   |     |   |
+   |     -------------------------------------------------^^^^^^^^^^^^^^^^^---
+   |     |   |                                            |
+   |     |   |                                            second mutable borrow occurs here
    |     |   first borrow later used by call
    |     first mutable borrow occurs here
 
@@ -124,8 +129,9 @@ error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time
   --> $DIR/two-phase-surprise-no-conflict.rs:158:53
    |
 LL |     reg.register_univ(Box::new(CapturePass::new_mut(&mut reg.sess_mut)));
-   |     --- -------------                               ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
-   |     |   |
+   |     ------------------------------------------------^^^^^^^^^^^^^^^^^---
+   |     |   |                                           |
+   |     |   |                                           second mutable borrow occurs here
    |     |   first borrow later used by call
    |     first mutable borrow occurs here
 
@@ -143,8 +149,9 @@ error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time
   --> $DIR/two-phase-surprise-no-conflict.rs:162:44
    |
 LL |     reg.register_ref(&CapturePass::new_mut(&mut reg.sess_mut));
-   |     --- ------------                       ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
-   |     |   |
+   |     ---------------------------------------^^^^^^^^^^^^^^^^^--
+   |     |   |                                  |
+   |     |   |                                  second mutable borrow occurs here
    |     |   first borrow later used by call
    |     first mutable borrow occurs here
 
index 09beb18146070712fbc5345c7f7c5977183664ec..e8a6ad0995a0fa555fdd21ac4cfb519d89d9a4f0 100644 (file)
@@ -2,7 +2,7 @@ error[E0505]: cannot move out of `alloc` because it is borrowed
   --> $DIR/leak-alloc.rs:26:10
    |
 LL |     let boxed = Box::new_in(10, alloc.by_ref());
-   |                                 ----- borrow of `alloc` occurs here
+   |                                 -------------- borrow of `alloc` occurs here
 LL |     let theref = Box::leak(boxed);
 LL |     drop(alloc);
    |          ^^^^^ move out of `alloc` occurs here
index 2d6e83c9e82f9139964c8a6131ac41da900f5180..06b5ca407db111a44837820ef3c3b9239883cc97 100644 (file)
@@ -12,7 +12,7 @@ error[E0596]: cannot borrow `s` as mutable, as it is not declared as mutable
 LL |     let s = std::io::stdin();
    |         - help: consider changing this to be mutable: `mut s`
 LL |     to_fn_once(move|| { s.read_to_end(&mut Vec::new()); });
-   |                         ^ cannot borrow as mutable
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88118-2.rs b/src/test/ui/closures/2229_closure_analysis/issue-88118-2.rs
new file mode 100644 (file)
index 0000000..0cfb1a5
--- /dev/null
@@ -0,0 +1,24 @@
+// edition:2021
+// run-pass
+#![feature(if_let_guard)]
+#[allow(unused_must_use)]
+#[allow(dead_code)]
+
+fn print_error_count(registry: &Registry) {
+    |x: &Registry| {
+        match &x {
+            Registry if let _ = registry.try_find_description() => { }
+            //~^ WARNING: irrefutable `if let` guard pattern
+            _ => {}
+        }
+    };
+}
+
+struct Registry;
+impl Registry {
+    pub fn try_find_description(&self) {
+        unimplemented!()
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88118-2.stderr b/src/test/ui/closures/2229_closure_analysis/issue-88118-2.stderr
new file mode 100644 (file)
index 0000000..1568902
--- /dev/null
@@ -0,0 +1,12 @@
+warning: irrefutable `if let` guard pattern
+  --> $DIR/issue-88118-2.rs:10:29
+   |
+LL |             Registry if let _ = registry.try_find_description() => { }
+   |                             ^
+   |
+   = note: `#[warn(irrefutable_let_patterns)]` on by default
+   = note: this pattern will always match, so the guard is useless
+   = help: consider removing the guard and adding a `let` inside the match arm
+
+warning: 1 warning emitted
+
index d0c29c768eb61557e3d242f0bb639835eb129b5b..a6b2b2e50a3fabb1389633feaebe965a71ed7a26 100644 (file)
@@ -7,7 +7,7 @@ LL |     let z = &mut x;
    |             ^^^^^^ second mutable borrow occurs here
 LL |     z.use_mut();
 LL |     y.use_mut();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error: aborting due to previous error
 
index eddbd29c0ef8174874d956eb1fe1f94f725a57ad..1ee612184def2b2290d858d3b77edfc4345c1cfb 100644 (file)
@@ -2,8 +2,9 @@ error[E0499]: cannot borrow `v` as mutable more than once at a time
   --> $DIR/one_line.rs:3:12
    |
 LL |     v.push(v.pop().unwrap());
-   |     - ---- ^ second mutable borrow occurs here
-   |     | |
+   |     -------^^^^^^^----------
+   |     | |    |
+   |     | |    second mutable borrow occurs here
    |     | first borrow later used by call
    |     first mutable borrow occurs here
 
index d7b52063dc4db24c8bfea069cb12a9a6b54a15c3..8c9672fd7fc1cbd5f27c14f1d13148c38bbb5385 100644 (file)
@@ -2,7 +2,7 @@ error: overly complex generic constant
   --> $DIR/issue-67375.rs:7:17
    |
 LL |     inner: [(); { [|_: &T| {}; 0].len() }],
-   |                 ^^---------------^^^^^^^^
+   |                 ^^---------------------^^
    |                   |
    |                   unsupported operation in generic constant
    |
index 0d49d0e96c8540908508e46b7e3a651fc782ea28..35619594f75b86546248042bb6fb9004240e7c3c 100644 (file)
@@ -7,8 +7,11 @@ LL |         FOO => {},
 error: unreachable pattern
   --> $DIR/issue-78057.rs:14:9
    |
+LL |         FOO => {},
+   |         --- matches any value
+LL |
 LL |         _ => {}
-   |         ^
+   |         ^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/issue-78057.rs:1:9
index 89073f975e8809c238dd3a4b3fb5ee2efff6f0d5..b550ac54573f0cee998987262a91da6482d2470a 100644 (file)
@@ -11,7 +11,7 @@ error[E0658]: mutable references are not allowed in constants
   --> $DIR/const_let_assign3.rs:14:5
    |
 LL |     s.foo(3);
-   |     ^
+   |     ^^^^^^^^
    |
    = note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
    = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
diff --git a/src/test/ui/consts/try-operator.rs b/src/test/ui/consts/try-operator.rs
new file mode 100644 (file)
index 0000000..fe43b13
--- /dev/null
@@ -0,0 +1,23 @@
+// run-pass
+
+#![feature(try_trait_v2)]
+#![feature(const_trait_impl)]
+#![feature(const_try)]
+#![feature(const_convert)]
+
+fn main() {
+    const fn result() -> Result<bool, ()> {
+        Err(())?;
+        Ok(true)
+    }
+
+    const FOO: Result<bool, ()> = result();
+    assert_eq!(Err(()), FOO);
+
+    const fn option() -> Option<()> {
+        None?;
+        Some(())
+    }
+    const BAR: Option<()> = option();
+    assert_eq!(None, BAR);
+}
index 666172197ce9b01be64be88936a201bec1997282..0503fac4a6680440bae153cda23276c2add66a2d 100644 (file)
@@ -19,8 +19,9 @@ error[E0502]: cannot borrow `self` as mutable because it is also borrowed as imm
   --> $DIR/issue-34126.rs:6:18
    |
 LL |         self.run(&mut self);
-   |         ---- --- ^^^^^^^^^ mutable borrow occurs here
-   |         |    |
+   |         ---------^^^^^^^^^-
+   |         |    |   |
+   |         |    |   mutable borrow occurs here
    |         |    immutable borrow later used by call
    |         immutable borrow occurs here
 
index 1f578d18a1ce5947e5e9c6aa644f3eacf3ce4319..9562d94509ea8f479e44caaf578337b9a78aae1f 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `f.v` as mutable, as `f` is not declared as mutable
 LL |     let f = Foo { v: Vec::new() };
    |         - help: consider changing this to be mutable: `mut f`
 LL |     f.v.push("cat".to_string());
-   |     ^^^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0594]: cannot assign to `s.x`, as `s` is not declared as mutable
   --> $DIR/issue-35937.rs:16:5
index 6efac371c028ec01457a66a809293ff722734247..dd193458b3726bc8294c046191ddbee6ef279246 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `*self.s` as mutable, as it is behind a `&` referenc
 LL |     fn f(&self) {
    |          ----- help: consider changing this to be a mutable reference: `&mut self`
 LL |         self.s.push('x');
-   |         ^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |         ^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
index cb4981089310a765c16d0d382c07a11011c56fdb..8bf5c76977da5bc1fdfd869ea1dfdedd3f90941e 100644 (file)
@@ -5,7 +5,7 @@ LL |     s: &'a String
    |        ---------- help: consider changing this to be mutable: `&'a mut String`
 ...
 LL |         self.s.push('x');
-   |         ^^^^^^ cannot borrow as mutable
+   |         ^^^^^^^^^^^^^^^^ cannot borrow as mutable
 
 error: aborting due to previous error
 
index 67782578a2c6478e1ba2587e8cc8731a17fb342b..0e1e42261c42b5529a055d74b793da9d6314c254 100644 (file)
@@ -5,7 +5,7 @@ LL |     s: &'a String
    |        ---------- help: consider changing this to be mutable: `&'a mut String`
 ...
 LL |         self.s.push('x');
-   |         ^^^^^^ cannot borrow as mutable
+   |         ^^^^^^^^^^^^^^^^ cannot borrow as mutable
 
 error: aborting due to previous error
 
index db3e6b894264667b586b30ca336a8d058fcd11e6..a2d162f08a173cbf2d56be888ccb470da09eb97b 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `*f.s` as mutable, as it is behind a `&` reference
 LL | fn f(x: usize, f: &Foo) {
    |                   ---- help: consider changing this to be a mutable reference: `&mut Foo<'_>`
 LL |     f.s.push('x');
-   |     ^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |     ^^^^^^^^^^^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
index 73473406a9ace47c2e7ae44254dd704d75d5c79c..67703a1497f50c7e60d6ebde2f621e9bd917a065 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `*buf` as mutable, as it is behind a `&` reference
 LL |     let mut buf = &[1, 2, 3, 4];
    |                   ------------- help: consider changing this to be a mutable reference: `&mut [1, 2, 3, 4]`
 LL |     buf.iter_mut();
-   |     ^^^ `buf` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |     ^^^^^^^^^^^^^^ `buf` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
index fa8bee184a74f2c0781f1948bc28ea98a510aa5a..247fe4b5b07bd6dc63b71352d354eb1e693e09e3 100644 (file)
@@ -4,7 +4,7 @@ error[E0275]: overflow evaluating the requirement `K: Send`
 LL |     is_send::<A>();
    |     ^^^^^^^^^^^^
    |
-   = help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`recursion_limit`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`recursion_limit`)
 note: required because it appears within the type `J`
   --> $DIR/recursion_limit.rs:24:9
    |
index 8339cc291cf30171a1bee0cde18ef69b8d8d0837..658207a47c9aba6c1647c9255250a8218588fb69 100644 (file)
@@ -4,7 +4,7 @@ error[E0055]: reached the recursion limit while auto-dereferencing `J`
 LL |     let x: &Bottom = &t;
    |                      ^^ deref recursion limit reached
    |
-   = help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`recursion_limit_deref`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`recursion_limit_deref`)
 
 error[E0308]: mismatched types
   --> $DIR/recursion_limit_deref.rs:50:22
index 0c6fdebd5095518b2067a76b27b2a8c390c335f6..609488e4f2f93770348372c16ef7b177c992ac80 100644 (file)
@@ -7,7 +7,7 @@ LL |     ($t:tt $($tail:tt)*) => { recurse!($($tail)*) };
 LL |     recurse!(0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9);
    |     -------------------------------------------------- in this macro invocation
    |
-   = help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`recursion_limit_macro`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`recursion_limit_macro`)
    = note: this error originates in the macro `recurse` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
index 7fed27adaff2b0619b432f76162e4bda8f3991df..8d6a7f3721f0fab6e3431921ca493329585b4325 100644 (file)
@@ -2,7 +2,7 @@ error[E0505]: cannot move out of `a` because it is borrowed
   --> $DIR/drop-with-active-borrows-1.rs:4:10
    |
 LL |     let b: Vec<&str> = a.lines().collect();
-   |                        - borrow of `a` occurs here
+   |                        --------- borrow of `a` occurs here
 LL |     drop(a);
    |          ^ move out of `a` occurs here
 LL |     for s in &b {
index ffec9306b77712d4bcbccdc7599356eeba5e0546..24650dfac02d963e3d4744a30ef8a4e1f5fba421 100644 (file)
@@ -2,7 +2,7 @@ error[E0515]: cannot return value referencing local variable `raw_lines`
   --> $DIR/drop-with-active-borrows-2.rs:3:5
    |
 LL |     raw_lines.iter().map(|l| l.trim()).collect()
-   |     ---------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     |
    |     returns a value referencing data owned by the current function
    |     `raw_lines` is borrowed here
index 83718a1e27371f0140c0792addd2ed6273da9e8f..e296217026570208b02c4531fab814979ab02640 100644 (file)
@@ -16,12 +16,12 @@ LL |     fn foo() {}
    |     ^^^^^^^^
 help: disambiguate the associated function for candidate #1
    |
-LL |     Trait1::foo()
-   |     ~~~~~~~~
+LL |     <Test as Trait1>::foo()
+   |     ~~~~~~~~~~~~~~~~~~
 help: disambiguate the associated function for candidate #2
    |
-LL |     Trait2::foo()
-   |     ~~~~~~~~
+LL |     <Test as Trait2>::foo()
+   |     ~~~~~~~~~~~~~~~~~~
 
 error: aborting due to previous error
 
index 1b8c5760e65bf0012a60350cdeaad7591ccff2e7..a52c90962355f3cbefb795dc34be69209ae5a905 100644 (file)
@@ -4,7 +4,7 @@ error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
 LL |     ref_foo.foo();
    |             ^^^ deref recursion limit reached
    |
-   = help: consider adding a `#![recursion_limit="8"]` attribute to your crate (`E0055`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "8"]` attribute to your crate (`E0055`)
 
 error: aborting due to previous error
 
index 6beb29c57d52772f0bc4d98633023069760b59c0..1060675cd45f4937460cc0e655163faf136354be 100644 (file)
@@ -2,7 +2,7 @@ error[E0161]: cannot move a value of type dyn Bar: the size of dyn Bar cannot be
   --> $DIR/E0161.rs:29:5
    |
 LL |     x.f();
-   |     ^
+   |     ^^^^^
 
 error: aborting due to previous error
 
index 6beb29c57d52772f0bc4d98633023069760b59c0..1060675cd45f4937460cc0e655163faf136354be 100644 (file)
@@ -2,7 +2,7 @@ error[E0161]: cannot move a value of type dyn Bar: the size of dyn Bar cannot be
   --> $DIR/E0161.rs:29:5
    |
 LL |     x.f();
-   |     ^
+   |     ^^^^^
 
 error: aborting due to previous error
 
index 6beb29c57d52772f0bc4d98633023069760b59c0..1060675cd45f4937460cc0e655163faf136354be 100644 (file)
@@ -2,7 +2,7 @@ error[E0161]: cannot move a value of type dyn Bar: the size of dyn Bar cannot be
   --> $DIR/E0161.rs:29:5
    |
 LL |     x.f();
-   |     ^
+   |     ^^^^^
 
 error: aborting due to previous error
 
index 6beb29c57d52772f0bc4d98633023069760b59c0..1060675cd45f4937460cc0e655163faf136354be 100644 (file)
@@ -2,7 +2,7 @@ error[E0161]: cannot move a value of type dyn Bar: the size of dyn Bar cannot be
   --> $DIR/E0161.rs:29:5
    |
 LL |     x.f();
-   |     ^
+   |     ^^^^^
 
 error: aborting due to previous error
 
index 15d851aa9340d8feeef8ec31170e08a542bc5e32..e13f0961a18cf2e00de92686f04ed559d01f65b9 100644 (file)
@@ -4,7 +4,7 @@ error[E0275]: overflow evaluating the requirement `Bar<Bar<Bar<Bar<Bar<Bar<Bar<B
 LL | impl<T> Foo for T where Bar<T>: Foo {}
    |                                 ^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`E0275`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`E0275`)
 note: required because of the requirements on the impl of `Foo` for `Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<Bar<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
   --> $DIR/E0275.rs:5:9
    |
index 567fc879040d3ffa1d2a17d850e55a45ea1463d5..6f6d1ff6a8f417c806a33073329a9459beb8b48e 100644 (file)
@@ -2,7 +2,10 @@ error[E0407]: method `b` is not a member of trait `Foo`
   --> $DIR/E0407.rs:9:5
    |
 LL |     fn b() {}
-   |     ^^^^^^^^^ not a member of trait `Foo`
+   |     ^^^-^^^^^
+   |     |  |
+   |     |  help: there is an associated function with a similar name: `a`
+   |     not a member of trait `Foo`
 
 error: aborting due to previous error
 
index d56baf7227201bd27241b6fc41f1ab16f0e1bb34..af5a1e186332fcd4572521f554402f0817e12694 100644 (file)
@@ -7,7 +7,7 @@ LL |     let mut a = &mut i;
    |                 ^^^^^^ second mutable borrow occurs here
 LL |     a.use_mut();
 LL |     x.use_mut();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error: aborting due to previous error
 
index e5671ee49e65470c0e56afe6727e7df7ba167df0..a3c7ef761898938a7df9d942fe0fc41bd27c3958 100644 (file)
@@ -6,7 +6,7 @@ LL |     let ref y = a;
 LL |     bar(a);
    |         ^ mutable borrow occurs here
 LL |     y.use_ref();
-   |     - immutable borrow later used here
+   |     ----------- immutable borrow later used here
 
 error: aborting due to previous error
 
index cade6d71852f853f8aa58be1e7c6f9c4d119c320..94cc89754db5fc615509c571e3794e7aab64672c 100644 (file)
@@ -6,7 +6,7 @@ LL |     let ref y = a;
 LL |     bar(a);
    |     ^^^^^^ mutable borrow occurs here
 LL |     y.use_ref();
-   |     - immutable borrow later used here
+   |     ----------- immutable borrow later used here
 
 error: aborting due to previous error
 
index 106dda2bc22605c3a6f4f5d8bf5e07c50b72eac9..fafe363eb47a6c05ecfa81883c917171d6eac6a6 100644 (file)
@@ -6,7 +6,7 @@ LL |     let _borrow = &mut value;
 LL |     let _sum = value + 1;
    |                ^^^^^ use of borrowed `value`
 LL |     _borrow.use_mut();
-   |     ------- borrow later used here
+   |     ----------------- borrow later used here
 
 error: aborting due to previous error
 
index 4d9d1ef121c6945f33b95eabf066faf60d3c86e0..bd3f37f54e0a88486417d2a7b786fcc1f2e42d7c 100644 (file)
@@ -6,7 +6,7 @@ LL |         let _ref_to_val: &Value = &x;
 LL |         eat(x);
    |             ^ move out of `x` occurs here
 LL |         _ref_to_val.use_ref();
-   |         ----------- borrow later used here
+   |         --------------------- borrow later used here
 
 error: aborting due to previous error
 
index cd5e467944bcdff8e7c8a3555cdf087a60f29c89..3837e206169d4756ccfad11ece429cd4f24d937c 100644 (file)
@@ -2,7 +2,7 @@ error[E0507]: cannot move out of dereference of `Ref<'_, TheDarkKnight>`
   --> $DIR/E0507.rs:12:5
    |
 LL |     x.borrow().nothing_is_true();
-   |     ^^^^^^^^^^ move occurs because value has type `TheDarkKnight`, which does not implement the `Copy` trait
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `TheDarkKnight`, which does not implement the `Copy` trait
 
 error: aborting due to previous error
 
index ecf92e7e3ae79c179026abd44406f8d90e66706f..b0756eb5589b7a6e5d616040ecba56642bcc49f0 100644 (file)
@@ -5,7 +5,7 @@ LL |     let z = &mut y;
    |             ------ mutable borrow occurs here
 ...
 LL |         r = y.as_ref().unwrap();
-   |             ^ immutable borrow occurs here
+   |             ^^^^^^^^^^ immutable borrow occurs here
 LL |
 LL |     }
    |     - mutable borrow might be used here, when `g` is dropped and runs the destructor for generator
index 8bb860f288f109d2ef2785a6dd4a008a3d7b8e75..7bb188352d7a27b7631deb78e7b5fe20194c3377 100644 (file)
@@ -2,7 +2,7 @@ error[E0597]: `*cell` does not live long enough
   --> $DIR/dropck.rs:10:40
    |
 LL |     let ref_ = Box::leak(Box::new(Some(cell.borrow_mut())));
-   |                                        ^^^^ borrowed value does not live long enough
+   |                                        ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
 ...
 LL | }
    | -
diff --git a/src/test/ui/hashmap/hashmap-iter-value-lifetime.nll.stderr b/src/test/ui/hashmap/hashmap-iter-value-lifetime.nll.stderr
deleted file mode 100644 (file)
index 312a91a..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-error[E0502]: cannot borrow `my_stuff` as mutable because it is also borrowed as immutable
-  --> $DIR/hashmap-iter-value-lifetime.rs:7:5
-   |
-LL |     let (_, thing) = my_stuff.iter().next().unwrap();
-   |                      -------- immutable borrow occurs here
-LL | 
-LL |     my_stuff.clear();
-   |     ^^^^^^^^ mutable borrow occurs here
-LL | 
-LL |     println!("{}", *thing);
-   |                    ------ immutable borrow later used here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0502`.
index f7626b13bad3640c325c95c86c24ddf9f0d1c14b..0724fec905524d7cbc0d3f77662d6aa5daf1a424 100644 (file)
@@ -2,7 +2,7 @@ error[E0502]: cannot borrow `my_stuff` as mutable because it is also borrowed as
   --> $DIR/hashmap-iter-value-lifetime.rs:7:5
    |
 LL |     let (_, thing) = my_stuff.iter().next().unwrap();
-   |                      -------- immutable borrow occurs here
+   |                      --------------- immutable borrow occurs here
 LL | 
 LL |     my_stuff.clear();
    |     ^^^^^^^^^^^^^^^^ mutable borrow occurs here
diff --git a/src/test/ui/hashmap/hashmap-lifetimes.nll.stderr b/src/test/ui/hashmap/hashmap-lifetimes.nll.stderr
deleted file mode 100644 (file)
index aa8e890..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-error[E0502]: cannot borrow `my_stuff` as mutable because it is also borrowed as immutable
-  --> $DIR/hashmap-lifetimes.rs:6:5
-   |
-LL |     let mut it = my_stuff.iter();
-   |                  -------- immutable borrow occurs here
-LL |     my_stuff.insert(1, 43);
-   |     ^^^^^^^^ mutable borrow occurs here
-LL |     it;
-   |     -- immutable borrow later used here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0502`.
index 497c7d1216cd961b7ec1885c094cc633f9666f9b..d1bcd53ae3b80c9ee8d5295c0b75ce091ae9b0c8 100644 (file)
@@ -2,7 +2,7 @@ error[E0502]: cannot borrow `my_stuff` as mutable because it is also borrowed as
   --> $DIR/hashmap-lifetimes.rs:6:5
    |
 LL |     let mut it = my_stuff.iter();
-   |                  -------- immutable borrow occurs here
+   |                  --------------- immutable borrow occurs here
 LL |     my_stuff.insert(1, 43);
    |     ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
 LL |     it;
index 439a113ef38112226b3cac1ec5e0792d7351e74b..87d826021b703f6a00fcbf208e6d2d549806a56b 100644 (file)
@@ -8,8 +8,8 @@ LL | / check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u3
 LL | | for<'a>    fn(&'a u32, &'a u32) -> &'a u32) }
    | |_____________________________________________- in this macro invocation
    |
-   = note: expected enum `Option<for<'r, 's> fn(&'r u32, &'s u32) -> &'r u32>`
-              found enum `Option<for<'r> fn(&'r u32, &'r u32) -> &'r u32>`
+   = note: expected enum `Option<for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32>`
+              found enum `Option<for<'a> fn(&'a u32, &'a u32) -> &'a u32>`
    = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
index 61b3f0ca2847c60b75b9d92d230e3a51bcc1a8cf..bd97f6f090646399877a4762eb5053ee2b5445a4 100644 (file)
@@ -8,7 +8,7 @@ LL | / check! { bound_a_vs_free_x: (for<'a> fn(&'a u32),
 LL | | fn(&'x u32)) }
    | |______________- in this macro invocation
    |
-   = note: expected enum `Option<for<'r> fn(&'r u32)>`
+   = note: expected enum `Option<for<'a> fn(&'a u32)>`
               found enum `Option<fn(&u32)>`
    = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info)
 
index 75e2ba58f33fbe9391347a3e349f1341272205d3..874909bf486c8c9c05cee57c934343d9ef48b75f 100644 (file)
@@ -8,8 +8,8 @@ LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>),
 LL | | for<'a>    fn(Inv<'a>, Inv<'a>)) }
    | |__________________________________- in this macro invocation
    |
-   = note: expected enum `Option<for<'r, 's> fn(Inv<'r>, Inv<'s>)>`
-              found enum `Option<for<'r> fn(Inv<'r>, Inv<'r>)>`
+   = note: expected enum `Option<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>`
+              found enum `Option<for<'a> fn(Inv<'a>, Inv<'a>)>`
    = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0308]: mismatched types
@@ -22,8 +22,8 @@ LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>),
 LL | | for<'a>    fn(Inv<'a>, Inv<'a>)) }
    | |__________________________________- in this macro invocation
    |
-   = note: expected enum `Option<for<'r, 's> fn(Inv<'r>, Inv<'s>)>`
-              found enum `Option<for<'r> fn(Inv<'r>, Inv<'r>)>`
+   = note: expected enum `Option<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>`
+              found enum `Option<for<'a> fn(Inv<'a>, Inv<'a>)>`
    = note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
index 70d5b3c2ec58c2b8659c0d459a7bff3d084159a3..fa391ecba8a9510a686be3cf2057f324bd28480f 100644 (file)
@@ -2,9 +2,9 @@ error[E0499]: cannot borrow `foo` as mutable more than once at a time
   --> $DIR/hrtb-debruijn-in-receiver.rs:17:5
    |
 LL |     foo.insert();
-   |     --- first mutable borrow occurs here
+   |     ------------ first mutable borrow occurs here
 LL |     foo.insert();
-   |     ^^^
+   |     ^^^^^^^^^^^^
    |     |
    |     second mutable borrow occurs here
    |     first borrow later used here
index 6e4fecf0ce4b234fad77637e771efae6104e0e1f..517b1ff598888e963ac0218f7411b70905aecdd1 100644 (file)
@@ -2,7 +2,10 @@ error[E0407]: method `method` is not a member of trait `Tr`
   --> $DIR/assoc_item_ctxt.rs:35:13
    |
 LL |             fn method() {}
-   |             ^^^^^^^^^^^^^^ not a member of trait `Tr`
+   |             ^^^------^^^^^
+   |             |  |
+   |             |  help: there is an associated function with a similar name: `method`
+   |             not a member of trait `Tr`
 ...
 LL |     mac_trait_impl!();
    |     ------------------ in this macro invocation
index fb90825c0d93db9ab2c77ed49f2f0323ae265c54..bc13aa62f4d682faa393905e6c6ceecf554fc7d5 100644 (file)
@@ -7,7 +7,7 @@ LL |     let S { 0: ref mut borrow2 } = s;
    |                ^^^^^^^^^^^^^^^ second mutable borrow occurs here
 ...
 LL |     borrow1.use_mut();
-   |     ------- first borrow later used here
+   |     ----------------- first borrow later used here
 
 error: aborting due to previous error
 
index c2497f8ff74d2629ef0f0a00e6a2e384a5bfa5f5..6c8b707b8e2f14d7d6db5109bf6bc3e759c36640 100644 (file)
@@ -4,7 +4,7 @@ error[E0425]: cannot find function `f` in this scope
 LL |         f();
    |         ^ not found in this scope
    |
-help: consider importing one of these items
+help: consider importing this function
    |
 LL | use foo::f;
    |
@@ -37,7 +37,7 @@ LL | n!(f);
 LL |         n!(f);
    |            ^ not found in this scope
    |
-   = note: consider importing one of these items:
+   = note: consider importing this function:
            foo::f
    = note: this error originates in the macro `n` (in Nightly builds, run with -Z macro-backtrace for more info)
 
@@ -50,7 +50,7 @@ LL | n!(f);
 LL |                 f
    |                 ^ not found in this scope
    |
-   = note: consider importing one of these items:
+   = note: consider importing this function:
            foo::f
    = note: this error originates in the macro `n` (in Nightly builds, run with -Z macro-backtrace for more info)
 
index 2c7a8ad5c1adb12a77b5e7337d2dd790046c7939..3b66a5e3150502600768bc6a95f71873983ef991 100644 (file)
@@ -4,10 +4,11 @@ error[E0425]: cannot find function `fpriv` in this scope
 LL |     fpriv();
    |     ^^^^^ not found in this scope
    |
-help: consider importing this function
-   |
-LL | use bar::fpriv;
+note: function `bar::fpriv` exists but is inaccessible
+  --> $DIR/glob-resolve1.rs:7:5
    |
+LL |     fn fpriv() {}
+   |     ^^^^^^^^^^ not accessible
 
 error[E0425]: cannot find function `epriv` in this scope
   --> $DIR/glob-resolve1.rs:27:5
@@ -15,10 +16,11 @@ error[E0425]: cannot find function `epriv` in this scope
 LL |     epriv();
    |     ^^^^^ not found in this scope
    |
-help: consider importing this function
-   |
-LL | use bar::epriv;
+note: function `bar::epriv` exists but is inaccessible
+  --> $DIR/glob-resolve1.rs:9:9
    |
+LL |         fn epriv();
+   |         ^^^^^^^^^^^ not accessible
 
 error[E0423]: expected value, found enum `B`
   --> $DIR/glob-resolve1.rs:28:5
@@ -44,10 +46,11 @@ error[E0425]: cannot find value `C` in this scope
 LL |     C;
    |     ^ not found in this scope
    |
-help: consider importing this unit struct
-   |
-LL | use bar::C;
+note: unit struct `bar::C` exists but is inaccessible
+  --> $DIR/glob-resolve1.rs:18:5
    |
+LL |     struct C;
+   |     ^^^^^^^^^ not accessible
 
 error[E0425]: cannot find function `import` in this scope
   --> $DIR/glob-resolve1.rs:30:5
@@ -67,16 +70,13 @@ LL |     pub enum B {
    |     ---------- similarly named enum `B` defined here
 ...
 LL |     foo::<A>();
-   |           ^
-   |
-help: an enum with a similar name exists
+   |           ^ help: an enum with a similar name exists: `B`
    |
-LL |     foo::<B>();
-   |           ~
-help: consider importing this enum
-   |
-LL | use bar::A;
+note: enum `bar::A` exists but is inaccessible
+  --> $DIR/glob-resolve1.rs:11:5
    |
+LL |     enum A {
+   |     ^^^^^^ not accessible
 
 error[E0412]: cannot find type `C` in this scope
   --> $DIR/glob-resolve1.rs:33:11
@@ -85,16 +85,13 @@ LL |     pub enum B {
    |     ---------- similarly named enum `B` defined here
 ...
 LL |     foo::<C>();
-   |           ^
-   |
-help: an enum with a similar name exists
-   |
-LL |     foo::<B>();
-   |           ~
-help: consider importing this struct
+   |           ^ help: an enum with a similar name exists: `B`
    |
-LL | use bar::C;
+note: struct `bar::C` exists but is inaccessible
+  --> $DIR/glob-resolve1.rs:18:5
    |
+LL |     struct C;
+   |     ^^^^^^^^^ not accessible
 
 error[E0412]: cannot find type `D` in this scope
   --> $DIR/glob-resolve1.rs:34:11
@@ -103,16 +100,13 @@ LL |     pub enum B {
    |     ---------- similarly named enum `B` defined here
 ...
 LL |     foo::<D>();
-   |           ^
-   |
-help: an enum with a similar name exists
-   |
-LL |     foo::<B>();
-   |           ~
-help: consider importing this type alias
+   |           ^ help: an enum with a similar name exists: `B`
    |
-LL | use bar::D;
+note: type alias `bar::D` exists but is inaccessible
+  --> $DIR/glob-resolve1.rs:20:5
    |
+LL |     type D = isize;
+   |     ^^^^^^^^^^^^^^^ not accessible
 
 error: aborting due to 8 previous errors
 
index a86ec7fabea4bf37690c55ff9175e20eacb00782..4c94634ee60f7d459959efe76f13adcfbfe5042c 100644 (file)
@@ -4,10 +4,11 @@ error[E0412]: cannot find type `Bar` in this scope
 LL |         fn sub() -> Bar { 1 }
    |                     ^^^ not found in this scope
    |
-help: consider importing this type alias
-   |
-LL |         use a::b::Bar;
+note: type alias `a::b::Bar` exists but is inaccessible
+  --> $DIR/issue-4366-2.rs:11:9
    |
+LL |         type Bar = isize;
+   |         ^^^^^^^^^^^^^^^^^ not accessible
 
 error[E0423]: expected function, found module `foo`
   --> $DIR/issue-4366-2.rs:25:5
index 1f26ba30000b43f4619a7f61d7fc6f1b73a8af0d..03e4718f5dfef880bba58ea20411f1ee116c14fc 100644 (file)
@@ -12,7 +12,7 @@ error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
 LL |     Foo.foo;
    |     ^^^^^^^ deref recursion limit reached
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`infinite_autoderef`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
 
 error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
   --> $DIR/infinite-autoderef.rs:25:9
@@ -20,7 +20,7 @@ error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
 LL |     Foo.foo;
    |         ^^^ deref recursion limit reached
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`infinite_autoderef`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
 
 error[E0609]: no field `foo` on type `Foo`
   --> $DIR/infinite-autoderef.rs:25:9
@@ -34,7 +34,7 @@ error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
 LL |     Foo.bar();
    |         ^^^ deref recursion limit reached
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`infinite_autoderef`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
 
 error[E0599]: no method named `bar` found for struct `Foo` in the current scope
   --> $DIR/infinite-autoderef.rs:26:9
index c7d9118d3f3a879d19860fdd5cdb9309cb50b183..15654dfaf88f154b3d9cfd2e2037b886767f0d25 100644 (file)
@@ -7,7 +7,7 @@ LL |     () => (recursive!())
 LL |     recursive!()
    |     ------------ in this macro invocation
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`infinite_macro_expansion`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_macro_expansion`)
    = note: this error originates in the macro `recursive` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
index 8ad921027e2e370d01e62519df91f6cf254fbd3e..6f72b79f2a5e08f3d6d13a0c11c7c3e8d882cc9f 100644 (file)
@@ -1,13 +1,14 @@
 error[E0515]: cannot return value referencing local variable `rawLines`
   --> $DIR/issue-13497-2.rs:3:5
    |
-LL |       rawLines
-   |       ^-------
-   |       |
-   |  _____`rawLines` is borrowed here
-   | |
-LL | |         .iter().map(|l| l.trim()).collect()
-   | |___________________________________________^ returns a value referencing data owned by the current function
+LL |        rawLines
+   |   _____^
+   |  |_____|
+   | ||
+LL | ||         .iter().map(|l| l.trim()).collect()
+   | ||_______________-___________________________^ returns a value referencing data owned by the current function
+   | |________________|
+   |                  `rawLines` is borrowed here
 
 error: aborting due to previous error
 
index a63bcd9ba5c760f650ea2fe6ed2763547daf0a69..64280219d75920732119a815b77beae7b6728dec 100644 (file)
@@ -7,7 +7,7 @@ LL |             $n + prob1!($n - 1);
 LL |     println!("Problem 1: {}", prob1!(1000));
    |                               ------------ in this macro invocation
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_16098`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_16098`)
    = note: this error originates in the macro `prob1` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
index 92d53088442e65b117c7f672fc32fd7078f4fffd..92e0f60079f72078831c52035ccfbb776c69b991 100644 (file)
@@ -4,7 +4,7 @@ error[E0275]: overflow evaluating the requirement `_: Sized`
 LL |     0.contains(bits);
    |       ^^^^^^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_18400`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_18400`)
 note: required because of the requirements on the impl of `Set<&[_]>` for `{integer}`
   --> $DIR/issue-18400.rs:6:16
    |
index af509aa59d481fdf8a37c5370e04b6818d5b631a..def032ba1bb7a785f513f8ff9cd04bfe7cbc622e 100644 (file)
@@ -1,8 +1,10 @@
 error[E0596]: cannot borrow data in a `&` reference as mutable
-  --> $DIR/issue-19163.rs:9:14
+  --> $DIR/issue-19163.rs:9:5
    |
 LL |     mywrite!(&v, "Hello world");
-   |              ^^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
+   |
+   = note: this error originates in the macro `mywrite` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
 
index 572c8ee33ce3c6cb50acc906249626117774e674..9135c5ac36af5919552c93c0bc54bd14910ff5bf 100644 (file)
@@ -13,7 +13,7 @@ error[E0275]: overflow evaluating the requirement `NoData<NoData<NoData<NoData<N
 LL | impl<T> Foo for T where NoData<T>: Foo {
    |                                    ^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
 note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
   --> $DIR/issue-20413.rs:8:9
    |
@@ -33,7 +33,7 @@ error[E0275]: overflow evaluating the requirement `NoData<NoData<NoData<NoData<N
 LL | impl<T> Foo for T where NoData<T>: Foo {
    |                                    ^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
 note: required because of the requirements on the impl of `Foo` for `NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<NoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
   --> $DIR/issue-20413.rs:8:9
    |
@@ -53,7 +53,7 @@ error[E0275]: overflow evaluating the requirement `EvenLessData<AlmostNoData<Eve
 LL | impl<T> Bar for T where EvenLessData<T>: Baz {
    |                                          ^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
 note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
   --> $DIR/issue-20413.rs:28:9
    |
@@ -78,7 +78,7 @@ error[E0275]: overflow evaluating the requirement `EvenLessData<AlmostNoData<Eve
 LL | impl<T> Bar for T where EvenLessData<T>: Baz {
    |                                          ^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
 note: required because of the requirements on the impl of `Bar` for `AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
   --> $DIR/issue-20413.rs:28:9
    |
@@ -103,7 +103,7 @@ error[E0275]: overflow evaluating the requirement `AlmostNoData<EvenLessData<Alm
 LL | impl<T> Baz for T where AlmostNoData<T>: Bar {
    |                                          ^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
 note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
   --> $DIR/issue-20413.rs:36:9
    |
@@ -128,7 +128,7 @@ error[E0275]: overflow evaluating the requirement `AlmostNoData<EvenLessData<Alm
 LL | impl<T> Baz for T where AlmostNoData<T>: Bar {
    |                                          ^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_20413`)
 note: required because of the requirements on the impl of `Baz` for `EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<EvenLessData<AlmostNoData<T>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
   --> $DIR/issue-20413.rs:36:9
    |
index 84c7106e89016b39f4938b9a50594d6f03135b53..dab3c3d1797256b03dd7c005609f4a9ab557f22b 100644 (file)
@@ -5,7 +5,7 @@ LL | fn call_it<F>(f: F) where F: Fn() { f(); }
    |                  - change this to accept `FnMut` instead of `Fn`
 ...
 LL |         call_it(|| x.gen_mut());
-   |         -------    ^ cannot borrow as mutable
+   |         -------    ^^^^^^^^^^^ cannot borrow as mutable
    |         |
    |         expects `Fn` instead of `FnMut`
 
index 68a95dc265e821bc155fd880e1d0405336f16f89..b345e90178742ee0049c08d733b604f2a1df7a0d 100644 (file)
@@ -4,7 +4,7 @@ error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 LL |     type Next = <GetNext<T::Next> as Next>::Next;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_23122_2`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_23122_2`)
 note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>`
   --> $DIR/issue-23122-2.rs:8:15
    |
index b00a420bc37d386b73536d95fe39c86c8ff35a61..22631e7c2a332d17d73ed717f25a8da2f29f7f93 100644 (file)
@@ -2,7 +2,7 @@ error[E0596]: cannot borrow data in an index of `HashMap<String, Vec<String>>` a
   --> $DIR/issue-41726.rs:5:9
    |
 LL |         things[src.as_str()].sort();
-   |         ^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<String, Vec<String>>`
 
index d5a9d233bc96988146822ac5db5c4cf9b8907071..73cf8652f6d20afbb67b15bb51c042acc0097bc8 100644 (file)
@@ -4,9 +4,9 @@ error[E0502]: cannot borrow `*collection` as mutable because it is also borrowed
 LL |     let _a = &collection;
    |              ----------- immutable borrow occurs here
 LL |     collection.swap(1, 2);
-   |     ^^^^^^^^^^ mutable borrow occurs here
+   |     ^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
 LL |     _a.use_ref();
-   |     -- immutable borrow later used here
+   |     ------------ immutable borrow later used here
 
 error: aborting due to previous error
 
index 1fd69f6e77799c7738c0d6ca04bcd672690460de..626cb2999e11910cdf144f3d7e2cc985248632aa 100644 (file)
@@ -2,7 +2,7 @@ error[E0596]: cannot borrow data in an index of `Container` as mutable
   --> $DIR/issue-44405.rs:21:5
    |
 LL |     container[&mut val].test();
-   |     ^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `Container`
 
index b46c277d04566a585493ad19f7f63bfdbda1b0a5..eff1de3e01752c472e9b1e718574575058256c88 100644 (file)
@@ -2,7 +2,7 @@ error[E0502]: cannot borrow `heap` as immutable because it is also borrowed as m
   --> $DIR/issue-47646.rs:9:30
    |
 LL |     let borrow = heap.peek_mut();
-   |                  ---- mutable borrow occurs here
+   |                  --------------- mutable borrow occurs here
 LL | 
 LL |     match (borrow, ()) {
    |           ------------ a temporary with access to the mutable borrow is created here ...
index d231f621e59c7c1e05a4d3380b540c7a5f112154..d450675776268221c33639a0501498cbbe1015ea 100644 (file)
@@ -2,7 +2,7 @@ error[E0597]: `line` does not live long enough
   --> $DIR/issue-52126-assign-op-invariance.rs:34:28
    |
 LL |         let v: Vec<&str> = line.split_whitespace().collect();
-   |                            ^^^^ borrowed value does not live long enough
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
 ...
 LL |         acc += cnt2;
    |         --- borrow later used here
index fb242f738c87e4877b356a9419af9dd367423633..6f345f56d1aa112ff8f8ca44bf0cbcf56d63229e 100644 (file)
@@ -10,7 +10,7 @@ LL |     for l in bad_letters {
    |              help: consider borrowing to avoid moving into the for loop: `&bad_letters`
 ...
 LL |     bad_letters.push('s');
-   |     ^^^^^^^^^^^ value borrowed here after move
+   |     ^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
    |
 note: this function takes ownership of the receiver `self`, which moves `bad_letters`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
index d57f1b778df176db34d013dea24b35af61b12d18..54973cfa34e616167eaee6fe06db519e9ed2328a 100644 (file)
@@ -2,7 +2,7 @@ error[E0515]: cannot return value referencing function parameter `y`
   --> $DIR/issue-81584.rs:5:22
    |
 LL |             .map(|y| y.iter().map(|x| x + 1))
-   |                      -^^^^^^^^^^^^^^^^^^^^^^
+   |                      --------^^^^^^^^^^^^^^^
    |                      |
    |                      returns a value referencing data owned by the current function
    |                      `y` is borrowed here
index d785749afc9c20ad47e8a91c4b38e919f1a921fb..c0b958f2bf22195d1880bd50af08b8d4e6af5bd4 100644 (file)
@@ -1,9 +1,8 @@
-// Checks whether declaring a lang item with the wrong number
-// of generic arguments crashes the compiler (issue #83893, #87573, and part of #9307).
+// Checks that declaring a lang item with the wrong number
+// of generic arguments errors rather than crashing (issue #83893, #87573, part of #9307, #79559).
 
 #![feature(lang_items, no_core)]
 #![no_core]
-#![crate_type = "lib"]
 
 #[lang = "sized"]
 trait MySized {}
@@ -26,6 +25,14 @@ trait MyIndex<'a, T> {}
 //~^ ERROR parameter `T` is never used
 //~| ERROR parameter `U` is never used
 
+// When the `start` lang item is missing generics very odd things can happen, especially when
+// it comes to cross-crate monomorphization
+#[lang = "start"]
+//~^ ERROR `start` language item must be applied to a function with 1 generic argument [E0718]
+fn start(_: *const u8, _: isize, _: *const *const u8) -> isize {
+    0
+}
+
 fn ice() {
     // Use add
     let r = 5;
@@ -42,3 +49,6 @@ fn ice() {
     // Use phantomdata
     let _ = MyPhantomData::<(), i32>;
 }
+
+// use `start`
+fn main() {}
index add5938811c28635b04aecbdc798679a0cbb577d..df5a77850f14d866d6cccd22d1c8b8d9e87cc3dd 100644 (file)
@@ -1,5 +1,5 @@
 error[E0718]: `add` language item must be applied to a trait with 1 generic argument
-  --> $DIR/lang-item-generic-requirements.rs:11:1
+  --> $DIR/lang-item-generic-requirements.rs:10:1
    |
 LL | #[lang = "add"]
    | ^^^^^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL | trait MyAdd<'a, T> {}
    |            ------- this trait has 2 generic arguments
 
 error[E0718]: `drop_in_place` language item must be applied to a function with at least 1 generic argument
-  --> $DIR/lang-item-generic-requirements.rs:15:1
+  --> $DIR/lang-item-generic-requirements.rs:14:1
    |
 LL | #[lang = "drop_in_place"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | fn my_ptr_drop() {}
    |               - this function has 0 generic arguments
 
 error[E0718]: `index` language item must be applied to a trait with 1 generic argument
-  --> $DIR/lang-item-generic-requirements.rs:19:1
+  --> $DIR/lang-item-generic-requirements.rs:18:1
    |
 LL | #[lang = "index"]
    | ^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL | trait MyIndex<'a, T> {}
    |              ------- this trait has 2 generic arguments
 
 error[E0718]: `phantom_data` language item must be applied to a struct with 1 generic argument
-  --> $DIR/lang-item-generic-requirements.rs:23:1
+  --> $DIR/lang-item-generic-requirements.rs:22:1
    |
 LL | #[lang = "phantom_data"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -32,8 +32,17 @@ LL |
 LL | struct MyPhantomData<T, U>;
    |                     ------ this struct has 2 generic arguments
 
+error[E0718]: `start` language item must be applied to a function with 1 generic argument
+  --> $DIR/lang-item-generic-requirements.rs:30:1
+   |
+LL | #[lang = "start"]
+   | ^^^^^^^^^^^^^^^^^
+LL |
+LL | fn start(_: *const u8, _: isize, _: *const *const u8) -> isize {
+   |         - this function has 0 generic arguments
+
 error[E0392]: parameter `T` is never used
-  --> $DIR/lang-item-generic-requirements.rs:25:22
+  --> $DIR/lang-item-generic-requirements.rs:24:22
    |
 LL | struct MyPhantomData<T, U>;
    |                      ^ unused parameter
@@ -42,7 +51,7 @@ LL | struct MyPhantomData<T, U>;
    = help: if you intended `T` to be a const parameter, use `const T: usize` instead
 
 error[E0392]: parameter `U` is never used
-  --> $DIR/lang-item-generic-requirements.rs:25:25
+  --> $DIR/lang-item-generic-requirements.rs:24:25
    |
 LL | struct MyPhantomData<T, U>;
    |                         ^ unused parameter
@@ -50,7 +59,7 @@ LL | struct MyPhantomData<T, U>;
    = help: consider removing `U` or referring to it in a field
    = help: if you intended `U` to be a const parameter, use `const U: usize` instead
 
-error: aborting due to 6 previous errors
+error: aborting due to 7 previous errors
 
 Some errors have detailed explanations: E0392, E0718.
 For more information about an error, try `rustc --explain E0392`.
index 4a605cfb8621b0bb5a410f902572c0c82e888dce..bbf04c98436e8dd43c96080472079b4b26ce6ff1 100644 (file)
@@ -7,7 +7,7 @@ LL |     let mut x = vec![1].iter();
    |                 creates a temporary which is freed while still in use
 LL |
 LL |     x.use_mut();
-   |     - borrow later used here
+   |     ----------- borrow later used here
    |
    = note: consider using a `let` binding to create a longer lived value
    = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
index 33be98c64910d1efe6ad8d0a037be3ae0ae82f81..825c45b2434411e5b34e6fcdcad6c136b1af1ece 100644 (file)
@@ -1,11 +1,3 @@
-error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
-  --> $DIR/ex3-both-anon-regions-using-fn-items.rs:2:3
-   |
-LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
-   |                        - help: consider changing this to be mutable: `mut y`
-LL |   y.push(z);
-   |   ^ cannot borrow as mutable
-
 error: lifetime may not live long enough
   --> $DIR/ex3-both-anon-regions-using-fn-items.rs:2:3
    |
@@ -16,6 +8,14 @@ LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
 LL |   y.push(z);
    |   ^^^^^^^^^ argument requires that `'1` must outlive `'2`
 
+error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
+  --> $DIR/ex3-both-anon-regions-using-fn-items.rs:2:3
+   |
+LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
+   |                        - help: consider changing this to be mutable: `mut y`
+LL |   y.push(z);
+   |   ^^^^^^^^^ cannot borrow as mutable
+
 error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0596`.
index 3c95be95db0801c7c8cbe91e7f39e762e189fe62..78a828dde866ff245170947a777750bda0b2f282 100644 (file)
@@ -1,11 +1,3 @@
-error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
-  --> $DIR/ex3-both-anon-regions-using-trait-objects.rs:2:3
-   |
-LL | fn foo(x:Box<dyn Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
-   |                                  - help: consider changing this to be mutable: `mut y`
-LL |   y.push(z);
-   |   ^ cannot borrow as mutable
-
 error: lifetime may not live long enough
   --> $DIR/ex3-both-anon-regions-using-trait-objects.rs:2:3
    |
@@ -16,6 +8,14 @@ LL | fn foo(x:Box<dyn Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
 LL |   y.push(z);
    |   ^^^^^^^^^ argument requires that `'1` must outlive `'2`
 
+error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
+  --> $DIR/ex3-both-anon-regions-using-trait-objects.rs:2:3
+   |
+LL | fn foo(x:Box<dyn Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
+   |                                  - help: consider changing this to be mutable: `mut y`
+LL |   y.push(z);
+   |   ^^^^^^^^^ cannot borrow as mutable
+
 error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/lint/must_not_suspend/mutex.rs b/src/test/ui/lint/must_not_suspend/mutex.rs
new file mode 100644 (file)
index 0000000..596249b
--- /dev/null
@@ -0,0 +1,12 @@
+// edition:2018
+#![deny(must_not_suspend)]
+
+async fn other() {}
+
+pub async fn uhoh(m: std::sync::Mutex<()>) {
+    let _guard = m.lock().unwrap(); //~ ERROR `MutexGuard` held across
+    other().await;
+}
+
+fn main() {
+}
diff --git a/src/test/ui/lint/must_not_suspend/mutex.stderr b/src/test/ui/lint/must_not_suspend/mutex.stderr
new file mode 100644 (file)
index 0000000..4e0d934
--- /dev/null
@@ -0,0 +1,26 @@
+error: `MutexGuard` held across a suspend point, but should not be
+  --> $DIR/mutex.rs:7:9
+   |
+LL |     let _guard = m.lock().unwrap();
+   |         ^^^^^^
+LL |     other().await;
+   |     ------------- the value is held across this suspend point
+   |
+note: the lint level is defined here
+  --> $DIR/mutex.rs:2:9
+   |
+LL | #![deny(must_not_suspend)]
+   |         ^^^^^^^^^^^^^^^^
+note: Holding a MutexGuard across suspend points can cause deadlocks, delays, and cause Futures to not implement `Send`
+  --> $DIR/mutex.rs:7:9
+   |
+LL |     let _guard = m.lock().unwrap();
+   |         ^^^^^^
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+  --> $DIR/mutex.rs:7:9
+   |
+LL |     let _guard = m.lock().unwrap();
+   |         ^^^^^^
+
+error: aborting due to previous error
+
index b4cce3cfea2179a8f69c51497fd136ca45079ec1..6a5cc91963da089deb6a1f748a23aacd70d98e17 100644 (file)
@@ -47,7 +47,7 @@ error: reference to packed field is unaligned
   --> $DIR/unaligned_references.rs:32:17
    |
 LL |         let _ = good.data.clone();
-   |                 ^^^^^^^^^
+   |                 ^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523>
index 3fdc2da9f1e2e3f0c22ae2ed344937a965b2149e..5ac392914e57bba9a65ee603c86ee454175daaf5 100644 (file)
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |         _ => y,
    |              ^ one type is more general than the other
    |
-   = note: expected fn pointer `for<'r, 's> fn(&'r u8, &'s u8) -> &'r u8`
-              found fn pointer `for<'r> fn(&'r u8, &'r u8) -> &'r u8`
+   = note: expected fn pointer `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
+              found fn pointer `for<'a> fn(&'a u8, &'a u8) -> &'a u8`
 
 error: aborting due to previous error
 
index ad14d6b7521bcaf97090931114f75e66e350f341..355f0754ab1b8040308489cb31191a84491d7e05 100644 (file)
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 LL |         _ => y,
    |              ^ one type is more general than the other
    |
-   = note: expected trait object `dyn for<'r, 's> Foo<&'r u8, &'s u8>`
-              found trait object `dyn for<'r> Foo<&'r u8, &'r u8>`
+   = note: expected trait object `dyn for<'a, 'b> Foo<&'a u8, &'b u8>`
+              found trait object `dyn for<'a> Foo<&'a u8, &'a u8>`
 
 error[E0308]: mismatched types
   --> $DIR/old-lub-glb-object.rs:10:14
@@ -13,8 +13,8 @@ error[E0308]: mismatched types
 LL |         _ => y,
    |              ^ one type is more general than the other
    |
-   = note: expected trait object `dyn for<'r, 's> Foo<&'r u8, &'s u8>`
-              found trait object `dyn for<'r> Foo<&'r u8, &'r u8>`
+   = note: expected trait object `dyn for<'a, 'b> Foo<&'a u8, &'b u8>`
+              found trait object `dyn for<'a> Foo<&'a u8, &'a u8>`
 
 error: aborting due to 2 previous errors
 
index 9139775c805a22424010b12fb991f018a442ab46..7a1e62d49d35e4dc000950707871cf4578549c63 100644 (file)
@@ -7,7 +7,7 @@ macro_rules! a {
     (A) => (concat!("", a!()));
     (A, $($A:ident),*) => (concat!("", a!($($A),*)))
     //~^ ERROR recursion limit reached
-    //~| HELP consider adding
+    //~| HELP consider increasing the recursion limit
 }
 
 fn main() {
index e6067e333498808231d917468045546a09b144c4..aa7d33cfd11a0b4012cbd5f9a9d6f58b0647f52b 100644 (file)
@@ -7,7 +7,7 @@ LL |     (A, $($A:ident),*) => (concat!("", a!($($A),*)))
 LL |     a!(A, A, A, A, A, A, A, A, A, A, A);
    |     ------------------------------------ in this macro invocation
    |
-   = help: consider adding a `#![recursion_limit="30"]` attribute to your crate (`issue_84632_eager_expansion_recursion_limit`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "30"]` attribute to your crate (`issue_84632_eager_expansion_recursion_limit`)
    = note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
index 38affde5f6c3301ac07522fe34fea420cb56c99b..dc38972d1d09ef6ad8d090e051100cb8593a341f 100644 (file)
@@ -31,7 +31,7 @@ LL |         my_recursive_macro!();
 LL |     my_recursive_macro!();
    |     ---------------------- in this macro invocation
    |
-   = help: consider adding a `#![recursion_limit="8"]` attribute to your crate (`trace_faulty_macros`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "8"]` attribute to your crate (`trace_faulty_macros`)
    = note: this error originates in the macro `my_recursive_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: trace_macro
index d636a11a91cec18b0ad26820346396c2bf38806e..59c091e44eb87efa0e16a1111eab535d3618a5e9 100644 (file)
@@ -5,7 +5,7 @@ LL |     let _arg = match args.next() {
    |         ---- borrow later stored here
 LL |         Some(arg) => {
 LL |             match arg.to_str() {
-   |                   ^^^ borrowed value does not live long enough
+   |                   ^^^^^^^^^^^^ borrowed value does not live long enough
 ...
 LL |         }
    |         - `arg` dropped here while still borrowed
index e0a58aed8f447fdde4133044effc3a366bc239fd..4ba778e0ef7256fa8eb5947a131d53754da067dd 100644 (file)
@@ -16,12 +16,12 @@ LL |     fn foo() {}
    |     ^^^^^^^^
 help: disambiguate the associated function for candidate #1
    |
-LL |     A::foo();
-   |     ~~~
+LL |     <AB as A>::foo();
+   |     ~~~~~~~~~~~
 help: disambiguate the associated function for candidate #2
    |
-LL |     B::foo();
-   |     ~~~
+LL |     <AB as B>::foo();
+   |     ~~~~~~~~~~~
 
 error: aborting due to previous error
 
index 946e71ee5b9c04a4444d17b425f16de8ba8ecc21..b98f7a786612f9f774ab07ce26093fdf314c57c2 100644 (file)
@@ -6,7 +6,7 @@ LL |     let y = &mut x;
 LL |     Foo::bar(&x);
    |              ^^ immutable borrow occurs here
 LL |     y.use_mut();
-   |     - mutable borrow later used here
+   |     ----------- mutable borrow later used here
 
 error[E0499]: cannot borrow `x` as mutable more than once at a time
   --> $DIR/method-self-arg-2.rs:20:14
@@ -16,7 +16,7 @@ LL |     let y = &mut x;
 LL |     Foo::baz(&mut x);
    |              ^^^^^^ second mutable borrow occurs here
 LL |     y.use_mut();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/moves/issue-72649-uninit-in-loop.rs b/src/test/ui/moves/issue-72649-uninit-in-loop.rs
new file mode 100644 (file)
index 0000000..e6bc4e2
--- /dev/null
@@ -0,0 +1,74 @@
+// Regression test for issue #72649
+// Tests that we don't emit spurious
+// 'value moved in previous iteration of loop' message
+
+struct NonCopy;
+
+fn good() {
+    loop {
+        let value = NonCopy{};
+        let _used = value;
+    }
+}
+
+fn moved_here_1() {
+    loop {
+        let value = NonCopy{};
+        //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+        let _used = value;
+        //~^ NOTE value moved here
+        let _used2 = value; //~ ERROR use of moved value: `value`
+        //~^ NOTE value used here after move
+    }
+}
+
+fn moved_here_2() {
+    let value = NonCopy{};
+    //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+    loop {
+        let _used = value;
+        //~^ NOTE value moved here
+        loop {
+            let _used2 = value; //~ ERROR use of moved value: `value`
+            //~^ NOTE value used here after move
+        }
+    }
+}
+
+fn moved_loop_1() {
+    let value = NonCopy{};
+    //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+    loop {
+        let _used = value; //~ ERROR use of moved value: `value`
+        //~^ NOTE value moved here, in previous iteration of loop
+    }
+}
+
+fn moved_loop_2() {
+    let mut value = NonCopy{};
+    //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+    let _used = value;
+    value = NonCopy{};
+    loop {
+        let _used2 = value; //~ ERROR use of moved value: `value`
+        //~^ NOTE value moved here, in previous iteration of loop
+    }
+}
+
+fn uninit_1() {
+    loop {
+        let value: NonCopy;
+        let _used = value; //~ ERROR use of possibly-uninitialized variable: `value`
+        //~^ NOTE use of possibly-uninitialized `value`
+    }
+}
+
+fn uninit_2() {
+    let mut value: NonCopy;
+    loop {
+        let _used = value; //~ ERROR use of possibly-uninitialized variable: `value`
+        //~^ NOTE use of possibly-uninitialized `value`
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/moves/issue-72649-uninit-in-loop.stderr b/src/test/ui/moves/issue-72649-uninit-in-loop.stderr
new file mode 100644 (file)
index 0000000..076d3df
--- /dev/null
@@ -0,0 +1,58 @@
+error[E0382]: use of moved value: `value`
+  --> $DIR/issue-72649-uninit-in-loop.rs:20:22
+   |
+LL |         let value = NonCopy{};
+   |             ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+LL |
+LL |         let _used = value;
+   |                     ----- value moved here
+LL |
+LL |         let _used2 = value;
+   |                      ^^^^^ value used here after move
+
+error[E0382]: use of moved value: `value`
+  --> $DIR/issue-72649-uninit-in-loop.rs:32:26
+   |
+LL |     let value = NonCopy{};
+   |         ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+...
+LL |         let _used = value;
+   |                     ----- value moved here
+...
+LL |             let _used2 = value;
+   |                          ^^^^^ value used here after move
+
+error[E0382]: use of moved value: `value`
+  --> $DIR/issue-72649-uninit-in-loop.rs:42:21
+   |
+LL |     let value = NonCopy{};
+   |         ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+...
+LL |         let _used = value;
+   |                     ^^^^^ value moved here, in previous iteration of loop
+
+error[E0382]: use of moved value: `value`
+  --> $DIR/issue-72649-uninit-in-loop.rs:53:22
+   |
+LL |     let mut value = NonCopy{};
+   |         --------- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
+...
+LL |         let _used2 = value;
+   |                      ^^^^^ value moved here, in previous iteration of loop
+
+error[E0381]: use of possibly-uninitialized variable: `value`
+  --> $DIR/issue-72649-uninit-in-loop.rs:61:21
+   |
+LL |         let _used = value;
+   |                     ^^^^^ use of possibly-uninitialized `value`
+
+error[E0381]: use of possibly-uninitialized variable: `value`
+  --> $DIR/issue-72649-uninit-in-loop.rs:69:21
+   |
+LL |         let _used = value;
+   |                     ^^^^^ use of possibly-uninitialized `value`
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0381, E0382.
+For more information about an error, try `rustc --explain E0381`.
index eca6bb9296ddc91e989b16aa26ef65b732d38b28..57be5fb4d8a59160181fa33d80b780215bc14049 100644 (file)
@@ -75,7 +75,7 @@ error[E0505]: cannot move out of `mut_foo` because it is borrowed
   --> $DIR/move-fn-self-receiver.rs:50:5
    |
 LL |     let ret = mut_foo.use_mut_self();
-   |               ------- borrow of `mut_foo` occurs here
+   |               ---------------------- borrow of `mut_foo` occurs here
 LL |     mut_foo;
    |     ^^^^^^^ move out of `mut_foo` occurs here
 LL |     ret;
index d56e45db13d0e62879e24f684f10083c83a70b59..6046c076f2e5313ccd28cae6442a95848fd9d928 100644 (file)
@@ -6,7 +6,7 @@ LL |     let b1 = &mut *b;
 LL |     let b2 = &mut *b;
    |                    ^ second mutable borrow occurs here
 LL |     b1.use_mut();
-   |     -- first borrow later used here
+   |     ------------ first borrow later used here
 
 error: aborting due to previous error
 
index 245eaff4bb4ecef750c6183fc776ce1f38850b6b..cba284550b95a3cb818dd5ad8c693a3006c78669 100644 (file)
@@ -5,7 +5,7 @@ LL | fn func(arg: S) {
    |         --- help: consider changing this to be mutable: `mut arg`
 ...
 LL |     arg.mutate();
-   |     ^^^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `local` as mutable, as it is not declared as mutable
   --> $DIR/mut-suggestion.rs:20:5
@@ -14,7 +14,7 @@ LL |     let local = S;
    |         ----- help: consider changing this to be mutable: `mut local`
 ...
 LL |     local.mutate();
-   |     ^^^^^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^^^ cannot borrow as mutable
 
 error: aborting due to 2 previous errors
 
index 8eded8f28572e1fe3c322fd494a56fc36becc00f..e9d7ca953d65b6fd80717ad37d3c1c03bfc34e07 100644 (file)
@@ -8,7 +8,7 @@ LL |     || x;
    |     |
    |     immutable borrow occurs here
 LL |     r.use_mut();
-   |     - mutable borrow later used here
+   |     ----------- mutable borrow later used here
 
 error[E0499]: cannot borrow `x` as mutable more than once at a time
   --> $DIR/closure-access-spans.rs:11:5
@@ -20,7 +20,7 @@ LL |     || x = 2;
    |     |
    |     second mutable borrow occurs here
 LL |     r.use_mut();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error[E0500]: closure requires unique access to `x` but it is already borrowed
   --> $DIR/closure-access-spans.rs:17:5
@@ -32,7 +32,7 @@ LL |     || *x = 2;
    |     |
    |     closure construction occurs here
 LL |     r.use_mut();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error[E0503]: cannot use `x` because it was mutably borrowed
   --> $DIR/closure-access-spans.rs:23:13
@@ -42,7 +42,7 @@ LL |     let r = &mut x;
 LL |     move || x;
    |             ^ use of borrowed `x`
 LL |     r.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0505]: cannot move out of `x` because it is borrowed
   --> $DIR/closure-access-spans.rs:29:5
@@ -54,7 +54,7 @@ LL |     || x;
    |     |
    |     move out of `x` occurs here
 LL |     r.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0382]: borrow of moved value: `x`
   --> $DIR/closure-access-spans.rs:35:5
index fffbee4d4a8e1218dc501721d182f4e06188cbb6..bada4e1b84b52cdf25e9222f62022d9bdadd15b9 100644 (file)
@@ -8,7 +8,7 @@ LL |     let f = || x.len();
 LL |     let y = x;
    |             ^ move out of `x` occurs here
 LL |     f.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
   --> $DIR/closure-borrow-spans.rs:11:13
@@ -20,7 +20,7 @@ LL |     let f = || x;
 LL |     let y = &mut x;
    |             ^^^^^^ mutable borrow occurs here
 LL |     f.use_ref();
-   |     - immutable borrow later used here
+   |     ----------- immutable borrow later used here
 
 error[E0597]: `x` does not live long enough
   --> $DIR/closure-borrow-spans.rs:19:16
@@ -32,7 +32,7 @@ LL |         f = || x;
 LL |     }
    |     - `x` dropped here while still borrowed
 LL |     f.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0506]: cannot assign to `x` because it is borrowed
   --> $DIR/closure-borrow-spans.rs:26:5
@@ -44,7 +44,7 @@ LL |     let f = || x;
 LL |     x = 1;
    |     ^^^^^ assignment to borrowed `x` occurs here
 LL |     f.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0503]: cannot use `x` because it was mutably borrowed
   --> $DIR/closure-borrow-spans.rs:32:13
@@ -56,7 +56,7 @@ LL |     let f = || x = 0;
 LL |     let y = x;
    |             ^ use of borrowed `x`
 LL |     f.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
   --> $DIR/closure-borrow-spans.rs:38:13
@@ -68,7 +68,7 @@ LL |     let f = || x = 0;
 LL |     let y = &x;
    |             ^^ immutable borrow occurs here
 LL |     f.use_ref();
-   |     - mutable borrow later used here
+   |     ----------- mutable borrow later used here
 
 error[E0499]: cannot borrow `x` as mutable more than once at a time
   --> $DIR/closure-borrow-spans.rs:44:13
@@ -80,7 +80,7 @@ LL |     let f = || x = 0;
 LL |     let y = &mut x;
    |             ^^^^^^ second mutable borrow occurs here
 LL |     f.use_ref();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error[E0597]: `x` does not live long enough
   --> $DIR/closure-borrow-spans.rs:52:16
@@ -92,7 +92,7 @@ LL |         f = || x = 0;
 LL |     }
    |     - `x` dropped here while still borrowed
 LL |     f.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0506]: cannot assign to `x` because it is borrowed
   --> $DIR/closure-borrow-spans.rs:59:5
@@ -104,7 +104,7 @@ LL |     let f = || x = 0;
 LL |     x = 1;
    |     ^^^^^ assignment to borrowed `x` occurs here
 LL |     f.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0505]: cannot move out of `x` because it is borrowed
   --> $DIR/closure-borrow-spans.rs:65:13
@@ -116,7 +116,7 @@ LL |     let f = || *x = 0;
 LL |     let y = x;
    |             ^ move out of `x` occurs here
 LL |     f.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0501]: cannot borrow `x` as immutable because previous closure requires unique access
   --> $DIR/closure-borrow-spans.rs:71:13
@@ -128,7 +128,7 @@ LL |     let f = || *x = 0;
 LL |     let y = &x;
    |             ^^ second borrow occurs here
 LL |     f.use_ref();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error[E0501]: cannot borrow `x` as mutable because previous closure requires unique access
   --> $DIR/closure-borrow-spans.rs:77:13
@@ -140,7 +140,7 @@ LL |     let f = || *x = 0;
 LL |     let y = &mut x;
    |             ^^^^^^ second borrow occurs here
 LL |     f.use_ref();
-   |     - first borrow later used here
+   |     ----------- first borrow later used here
 
 error[E0597]: `x` does not live long enough
   --> $DIR/closure-borrow-spans.rs:86:16
@@ -152,7 +152,7 @@ LL |         f = || *x = 0;
 LL |     }
    |     - `x` dropped here while still borrowed
 LL |     f.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error[E0506]: cannot assign to `*x` because it is borrowed
   --> $DIR/closure-borrow-spans.rs:93:5
@@ -164,7 +164,7 @@ LL |     let f = || *x = 0;
 LL |     *x = 1;
    |     ^^^^^^ assignment to borrowed `*x` occurs here
 LL |     f.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error: aborting due to 14 previous errors
 
diff --git a/src/test/ui/nll/get_default.nll.stderr b/src/test/ui/nll/get_default.nll.stderr
deleted file mode 100644 (file)
index 2791230..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
-  --> $DIR/get_default.rs:21:17
-   |
-LL | fn ok(map: &mut Map) -> &String {
-   |            - let's call the lifetime of this reference `'1`
-LL |     loop {
-LL |         match map.get() {
-   |               --- immutable borrow occurs here
-LL |             Some(v) => {
-LL |                 return v;
-   |                        - returning this value requires that `*map` is borrowed for `'1`
-...
-LL |                 map.set(String::new()); // Ideally, this would not error.
-   |                 ^^^ mutable borrow occurs here
-
-error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
-  --> $DIR/get_default.rs:32:17
-   |
-LL | fn err(map: &mut Map) -> &String {
-   |             - let's call the lifetime of this reference `'1`
-LL |     loop {
-LL |         match map.get() {
-   |               --- immutable borrow occurs here
-LL |             Some(v) => {
-LL |                 map.set(String::new()); // Both AST and MIR error here
-   |                 ^^^ mutable borrow occurs here
-LL |
-LL |                 return v;
-   |                        - returning this value requires that `*map` is borrowed for `'1`
-
-error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
-  --> $DIR/get_default.rs:37:17
-   |
-LL | fn err(map: &mut Map) -> &String {
-   |             - let's call the lifetime of this reference `'1`
-LL |     loop {
-LL |         match map.get() {
-   |               --- immutable borrow occurs here
-...
-LL |                 return v;
-   |                        - returning this value requires that `*map` is borrowed for `'1`
-...
-LL |                 map.set(String::new()); // Ideally, just AST would error here
-   |                 ^^^ mutable borrow occurs here
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0502`.
index af79771e7e1b9acfaffaf421d3bfdb211b300ea5..6998c04336e5022ec3f67556bfe225ea904c7b8b 100644 (file)
@@ -5,7 +5,7 @@ LL | fn ok(map: &mut Map) -> &String {
    |            - let's call the lifetime of this reference `'1`
 LL |     loop {
 LL |         match map.get() {
-   |               --- immutable borrow occurs here
+   |               --------- immutable borrow occurs here
 LL |             Some(v) => {
 LL |                 return v;
    |                        - returning this value requires that `*map` is borrowed for `'1`
@@ -20,7 +20,7 @@ LL | fn err(map: &mut Map) -> &String {
    |             - let's call the lifetime of this reference `'1`
 LL |     loop {
 LL |         match map.get() {
-   |               --- immutable borrow occurs here
+   |               --------- immutable borrow occurs here
 LL |             Some(v) => {
 LL |                 map.set(String::new()); // Both AST and MIR error here
    |                 ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
@@ -35,7 +35,7 @@ LL | fn err(map: &mut Map) -> &String {
    |             - let's call the lifetime of this reference `'1`
 LL |     loop {
 LL |         match map.get() {
-   |               --- immutable borrow occurs here
+   |               --------- immutable borrow occurs here
 ...
 LL |                 return v;
    |                        - returning this value requires that `*map` is borrowed for `'1`
index 82cd364eeffd0c42d1c39428ae83a7343b020295..60ef3f7b85ec45f6717806e60bd7fe4e29636b9e 100644 (file)
@@ -2,10 +2,10 @@ error[E0499]: cannot borrow `**other` as mutable more than once at a time
   --> $DIR/issue-46589.rs:23:21
    |
 LL |         *other = match (*other).get_self() {
-   |                        -------- first mutable borrow occurs here
+   |                        ------------------- first mutable borrow occurs here
 LL |             Some(s) => s,
 LL |             None => (*other).new_self()
-   |                     ^^^^^^^^
+   |                     ^^^^^^^^^^^^^^^^^^^
    |                     |
    |                     second mutable borrow occurs here
    |                     first borrow later used here
index 18696f57c44aef80ac01115c80833feece008a1c..9f4e971f909570e8442cb1d6b034354184dcbf9e 100644 (file)
@@ -45,7 +45,7 @@ error[E0596]: cannot borrow data in a `&` reference as mutable
   --> $DIR/issue-51191.rs:22:9
    |
 LL |         (&mut self).bar();
-   |         ^^^^^^^^^^^ cannot borrow as mutable
+   |         ^^^^^^^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
   --> $DIR/issue-51191.rs:28:9
index db53e444b9e4a60dca5232390e5fad4fc2c9f309..807b95f7e1384fc1ccc0a9700a9b87eeab7fdd68 100644 (file)
@@ -7,7 +7,7 @@ LL |     a.b = B;
 LL |     foo(a);
    |         - value moved here
 LL |     a.b.clone()
-   |     ^^^ value borrowed here after move
+   |     ^^^^^^^^^^^ value borrowed here after move
 
 error: aborting due to previous error
 
index 45831460e5238e35b149d718352cfc74aeffd533..11cd423295aaef93134fe8341c34b93bab558b32 100644 (file)
@@ -7,7 +7,7 @@ LL |
 LL |     }
    |     - here, drop of `child` needs exclusive access to `*child.raw`, because the type `C<'_>` implements the `Drop` trait
 LL |     members.len();
-   |     ------- borrow later used here
+   |     ------------- borrow later used here
    |
    = note: consider using a `let` binding to create a longer lived value
 
index 70f063ca0e8334cc3aba00ff874dbc441887aa00..a8e1edc5497422311da7754564e32d8c29978c78 100644 (file)
@@ -2,7 +2,7 @@ error[E0597]: `counter` does not live long enough
   --> $DIR/issue-54556-niconii.rs:22:20
    |
 LL |     if let Ok(_) = counter.lock() { }
-   |                    ^^^^^^^-------
+   |                    ^^^^^^^^^^^^^^
    |                    |
    |                    borrowed value does not live long enough
    |                    a temporary with access to the borrow is created here ...
index 0db9fe62c3869d409c76cdb697b8b5015d35cff1..12e28aa3fba0ecdc624bce3b8f58e05372c3aaa3 100644 (file)
@@ -17,7 +17,7 @@ LL | fn to_refs<T>(mut list: [&mut List<T>; 2]) -> Vec<&mut T> {
    |                          - let's call the lifetime of this reference `'1`
 ...
 LL |         if let Some(n) = list[0].next.as_mut() {
-   |                          ^^^^^^^^^^^^---------
+   |                          ^^^^^^^^^^^^^^^^^^^^^
    |                          |
    |                          `list[_].next` was mutably borrowed here in the previous iteration of the loop
    |                          argument requires that `list[_].next` is borrowed for `'1`
index f1af2e855afe6c5fd501fc7054d496a579c3bdd6..4488431fc5737a32bca7db4ba418e9102d275c89 100644 (file)
@@ -17,7 +17,7 @@ LL | fn to_refs<'a, T>(mut list: (&'a mut List<T>, &'a mut List<T>)) -> Vec<&'a
    |            -- lifetime `'a` defined here
 ...
 LL |         if let Some(n) = (list.0).next.as_mut() {
-   |                          ^^^^^^^^^^^^^---------
+   |                          ^^^^^^^^^^^^^^^^^^^^^^
    |                          |
    |                          `list.0.next` was mutably borrowed here in the previous iteration of the loop
    |                          argument requires that `list.0.next` is borrowed for `'a`
index c0b97bea348c4b99c5b07be4ce1d53635ee7abd1..22c72af61d625e5f123fc3a55d3e4fc671c03ad9 100644 (file)
@@ -5,7 +5,7 @@ LL |     let slice = &mut data;
    |                 --------- first mutable borrow occurs here
 LL |     capitalize(slice);
 LL |     data.push('d');
-   |     ^^^^ second mutable borrow occurs here
+   |     ^^^^^^^^^^^^^^ second mutable borrow occurs here
 ...
 LL |     capitalize(slice);
    |                ----- first borrow later used here
@@ -17,7 +17,7 @@ LL |     let slice = &mut data;
    |                 --------- first mutable borrow occurs here
 ...
 LL |     data.push('e');
-   |     ^^^^ second mutable borrow occurs here
+   |     ^^^^^^^^^^^^^^ second mutable borrow occurs here
 ...
 LL |     capitalize(slice);
    |                ----- first borrow later used here
@@ -29,7 +29,7 @@ LL |     let slice = &mut data;
    |                 --------- first mutable borrow occurs here
 ...
 LL |     data.push('f');
-   |     ^^^^ second mutable borrow occurs here
+   |     ^^^^^^^^^^^^^^ second mutable borrow occurs here
 LL |
 LL |     capitalize(slice);
    |                ----- first borrow later used here
index 2fe6a53a49aefc47ad35a4c1b4f33191f3cba393..fd5e4531b2e673c646fe9652ad0b937c5ef2cab2 100644 (file)
@@ -17,7 +17,7 @@ LL | fn assignment_to_field_projection<'a, T>(
    |                                   -- lifetime `'a` defined here
 ...
 LL |         if let Some(n) = (list.0).next.as_mut() {
-   |                          ^^^^^^^^^^^^^---------
+   |                          ^^^^^^^^^^^^^^^^^^^^^^
    |                          |
    |                          `list.0.next` was mutably borrowed here in the previous iteration of the loop
    |                          argument requires that `list.0.next` is borrowed for `'a`
@@ -41,7 +41,7 @@ LL | fn assignment_through_projection_chain<'a, T>(
    |                                        -- lifetime `'a` defined here
 ...
 LL |         if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() {
-   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^---------
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                          |
    |                          `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop
    |                          argument requires that `list.0.0.0.0.0.next` is borrowed for `'a`
diff --git a/src/test/ui/nll/region-ends-after-if-condition.nll.stderr b/src/test/ui/nll/region-ends-after-if-condition.nll.stderr
deleted file mode 100644 (file)
index 3229309..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-error[E0502]: cannot borrow `my_struct.field` as mutable because it is also borrowed as immutable
-  --> $DIR/region-ends-after-if-condition.rs:26:9
-   |
-LL |     let value = &my_struct.field;
-   |                 ---------------- immutable borrow occurs here
-LL |     if value.is_empty() {
-LL |         my_struct.field.push_str("Hello, world!");
-   |         ^^^^^^^^^^^^^^^ mutable borrow occurs here
-...
-LL |     drop(value);
-   |          ----- immutable borrow later used here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0502`.
index 94def6900867795ca0adfe8246effba8e3c2c542..6256c4a01d386257c2dc537fef87f9f06a6241d3 100644 (file)
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
 LL |     let y: for<'a> fn(&'a ()) = x;
    |                                 ^ one type is more general than the other
    |
-   = note: expected fn pointer `for<'r> fn(&'r ())`
+   = note: expected fn pointer `for<'a> fn(&'a ())`
               found fn pointer `fn(&())`
 
 error: aborting due to previous error
index 8c1eaeb6aa7b9777aa61243a0b7a64a460976f64..b839015f97f6140e2734c20fe7332104b6912ae2 100644 (file)
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
 LL |     let a: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it();
    |                                                          ^^^^^^^^^ one type is more general than the other
    |
-   = note: expected fn pointer `for<'r, 's> fn(&'r u32, &'s u32) -> &'r u32`
+   = note: expected fn pointer `for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32`
               found fn pointer `for<'a> fn(&'a u32, &'a u32) -> &'a u32`
 
 error[E0308]: mismatched types
@@ -14,7 +14,7 @@ LL |     let _: for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32 = make_it();
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
    |
    = note: expected fn pointer `for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32`
-              found fn pointer `for<'r> fn(&'r u32, &'r u32) -> &'r u32`
+              found fn pointer `for<'a> fn(&'a u32, &'a u32) -> &'a u32`
 
 error: aborting due to 2 previous errors
 
index 60a7f204446806872f27e12670de0bfe46849759..6d144a4be6ed38e9d583efd1ead55311afdd6846 100644 (file)
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
 LL |     let y: Box<dyn for<'a> Foo<'a>> = x;
    |                                       ^ one type is more general than the other
    |
-   = note: expected trait object `dyn for<'r> Foo<'r>`
+   = note: expected trait object `dyn for<'a> Foo<'a>`
               found trait object `dyn Foo<'_>`
 
 error: aborting due to previous error
index efd56ea2dd5423ff6cf8aaa9ee803f80f89f9eea..bd2b8b158598ab559c846c4ac13c88800dec28bd 100644 (file)
@@ -5,10 +5,10 @@ LL |     let value = &mut my_struct.field;
    |                 -------------------- first mutable borrow occurs here
 LL |     loop {
 LL |         my_struct.field.push_str("Hello, world!");
-   |         ^^^^^^^^^^^^^^^ second mutable borrow occurs here
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
 LL |
 LL |         value.len();
-   |         ----- first borrow later used here
+   |         ----------- first borrow later used here
 
 error: aborting due to previous error
 
index 1497aa42082d17adc89b0dc6384aa3c8a8d5088c..7ccc0cbdd576bb225608fdf75289f7b0918989b0 100644 (file)
@@ -2,7 +2,7 @@ error[E0161]: cannot move a value of type dyn Bar: the size of dyn Bar cannot be
   --> $DIR/object-safety-by-value-self-use.rs:15:5
    |
 LL |     t.bar()
-   |     ^
+   |     ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/pattern/usefulness/const-private-fields.rs b/src/test/ui/pattern/usefulness/const-private-fields.rs
new file mode 100644 (file)
index 0000000..06c832c
--- /dev/null
@@ -0,0 +1,30 @@
+// check-pass
+//
+// Check that we don't ignore private fields in usefulness checking
+#![deny(unreachable_patterns)]
+
+mod inner {
+    #[derive(PartialEq, Eq)]
+    pub struct PrivateField {
+        pub x: bool,
+        y: bool,
+    }
+
+    pub const FOO: PrivateField = PrivateField { x: true, y: true };
+    pub const BAR: PrivateField = PrivateField { x: true, y: false };
+}
+use inner::*;
+
+fn main() {
+    match FOO {
+        FOO => {}
+        BAR => {}
+        _ => {}
+    }
+
+    match FOO {
+        FOO => {}
+        PrivateField { x: true, .. } => {}
+        _ => {}
+    }
+}
index 68451043cf5403918670794bb2c4cd63d298628f..05c009a6f3fe147b15eca27fbedade1cb8b4889d 100644 (file)
@@ -7,8 +7,11 @@ LL |         FOO => {}
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:32:9
    |
+LL |         FOO => {}
+   |         --- matches any value
+LL |
 LL |         _ => {} // should not be emitting unreachable warning
-   |         ^
+   |         ^ unreachable pattern
    |
 note: the lint level is defined here
   --> $DIR/consts-opaque.rs:6:9
@@ -25,8 +28,11 @@ LL |         FOO_REF => {}
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:39:9
    |
+LL |         FOO_REF => {}
+   |         ------- matches any value
+LL |
 LL |         Foo(_) => {} // should not be emitting unreachable warning
-   |         ^^^^^^
+   |         ^^^^^^ unreachable pattern
 
 warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
   --> $DIR/consts-opaque.rs:45:9
@@ -70,15 +76,18 @@ LL |         BAR => {}
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:63:9
    |
+LL |         BAR => {}
+   |         --- matches any value
+LL |
 LL |         Bar => {} // should not be emitting unreachable warning
-   |         ^^^
+   |         ^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:65:9
    |
-LL |         Bar => {} // should not be emitting unreachable warning
+LL |         BAR => {}
    |         --- matches any value
-LL |
+...
 LL |         _ => {}
    |         ^ unreachable pattern
 
@@ -97,14 +106,20 @@ LL |         BAR => {} // should not be emitting unreachable warning
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:72:9
    |
+LL |         BAR => {}
+   |         --- matches any value
+LL |
 LL |         BAR => {} // should not be emitting unreachable warning
-   |         ^^^
+   |         ^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:75:9
    |
+LL |         BAR => {}
+   |         --- matches any value
+...
 LL |         _ => {} // should not be emitting unreachable warning
-   |         ^
+   |         ^ unreachable pattern
 
 error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
   --> $DIR/consts-opaque.rs:80:9
@@ -115,14 +130,20 @@ LL |         BAZ => {}
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:82:9
    |
+LL |         BAZ => {}
+   |         --- matches any value
+LL |
 LL |         Baz::Baz1 => {} // should not be emitting unreachable warning
-   |         ^^^^^^^^^
+   |         ^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:84:9
    |
+LL |         BAZ => {}
+   |         --- matches any value
+...
 LL |         _ => {}
-   |         ^
+   |         ^ unreachable pattern
 
 error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
   --> $DIR/consts-opaque.rs:90:9
@@ -133,8 +154,11 @@ LL |         BAZ => {}
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:92:9
    |
+LL |         BAZ => {}
+   |         --- matches any value
+LL |
 LL |         _ => {}
-   |         ^
+   |         ^ unreachable pattern
 
 error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
   --> $DIR/consts-opaque.rs:97:9
@@ -145,20 +169,28 @@ LL |         BAZ => {}
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:99:9
    |
+LL |         BAZ => {}
+   |         --- matches any value
+LL |
 LL |         Baz::Baz2 => {} // should not be emitting unreachable warning
-   |         ^^^^^^^^^
+   |         ^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:101:9
    |
+LL |         BAZ => {}
+   |         --- matches any value
+...
 LL |         _ => {} // should not be emitting unreachable warning
-   |         ^
+   |         ^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:127:9
    |
+LL |         Wrap(_) => {}
+   |         ------- matches any value
 LL |         WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer
-   |         ^^^^^^^^
+   |         ^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/consts-opaque.rs:141:9
index 9a02fac6a75dd47f6a14e6e2a60d73efa50f70da..0ffb0ffd82aa0f6735b19c09b065240cd2aa573c 100644 (file)
@@ -133,8 +133,10 @@ LL |         5..15 => {},
 error: unreachable pattern
   --> $DIR/reachability.rs:83:9
    |
+LL |         _ => {},
+   |         - matches any value
 LL |         '\u{D7FF}'..='\u{E000}' => {},
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
 
 error: unreachable pattern
   --> $DIR/reachability.rs:104:9
index c873c20cca81bd2f8d70d1a6408a866a07c43603..48ed14915084a5d607ae44e57b8a0cedcc4a6d3b 100644 (file)
@@ -1,8 +1,8 @@
-error[E0004]: non-exhaustive patterns: `Box(_, _)` not covered
+error[E0004]: non-exhaustive patterns: `box _` not covered
   --> $DIR/issue-3601.rs:30:44
    |
 LL |         box NodeKind::Element(ed) => match ed.kind {
-   |                                            ^^^^^^^ pattern `Box(_, _)` not covered
+   |                                            ^^^^^^^ pattern `box _` not covered
    |
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
    = note: the matched value is of type `Box<ElementKind>`
diff --git a/src/test/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs b/src/test/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs
new file mode 100644 (file)
index 0000000..c1bfcc7
--- /dev/null
@@ -0,0 +1,6 @@
+// This used to ICE in exhaustiveness checking. Explanation here:
+// https://github.com/rust-lang/rust/issues/82772#issuecomment-905946768
+fn main() {
+    let Box { 1: _, .. }: Box<()>; //~ ERROR field `1` of
+    let Box { .. }: Box<()>;
+}
diff --git a/src/test/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr b/src/test/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr
new file mode 100644 (file)
index 0000000..2c8c85b
--- /dev/null
@@ -0,0 +1,9 @@
+error[E0451]: field `1` of struct `Box` is private
+  --> $DIR/issue-82772-match-box-as-struct.rs:4:15
+   |
+LL |     let Box { 1: _, .. }: Box<()>;
+   |               ^^^^ private field
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0451`.
index 943d51330976a014e5d92b3bf6ea46cc54d177d5..d45bfc3ca55ea19c99ed034623444f060013a0ca 100644 (file)
@@ -1,6 +1,6 @@
 error[E0275]: overflow evaluating the requirement `Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range<u8>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>: Iterator`
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_83150`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_83150`)
    = note: required because of the requirements on the impl of `Iterator` for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut std::ops::Range<u8>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>, [closure@$DIR/issue-83150.rs:10:24: 10:33]>`
 
 error: aborting due to previous error
diff --git a/src/test/ui/recursion_limit/no-value.rs b/src/test/ui/recursion_limit/no-value.rs
new file mode 100644 (file)
index 0000000..2202e5b
--- /dev/null
@@ -0,0 +1,6 @@
+// Test the parse error for no value provided to recursion_limit
+
+#![recursion_limit]
+//~^ ERROR malformed `recursion_limit` attribute input
+
+fn main() {}
diff --git a/src/test/ui/recursion_limit/no-value.stderr b/src/test/ui/recursion_limit/no-value.stderr
new file mode 100644 (file)
index 0000000..35ac2c4
--- /dev/null
@@ -0,0 +1,8 @@
+error: malformed `recursion_limit` attribute input
+  --> $DIR/no-value.rs:3:1
+   |
+LL | #![recursion_limit]
+   | ^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![recursion_limit = "N"]`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/recursion_limit/zero-overflow.rs b/src/test/ui/recursion_limit/zero-overflow.rs
new file mode 100644 (file)
index 0000000..77bd818
--- /dev/null
@@ -0,0 +1,7 @@
+//~ ERROR overflow evaluating the requirement `&mut Self: DispatchFromDyn<&mut RustaceansAreAwesome>
+//~| HELP consider increasing the recursion limit
+// build-fail
+
+#![recursion_limit = "0"]
+
+fn main() {}
diff --git a/src/test/ui/recursion_limit/zero-overflow.stderr b/src/test/ui/recursion_limit/zero-overflow.stderr
new file mode 100644 (file)
index 0000000..9007ec0
--- /dev/null
@@ -0,0 +1,7 @@
+error[E0275]: overflow evaluating the requirement `&mut Self: DispatchFromDyn<&mut RustaceansAreAwesome>`
+   |
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`zero_overflow`)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
index 6358805d89dee2c095f1cdccd16c83898598f45e..c85cbadea710140585d4a63272c85b4248b1434a 100644 (file)
@@ -4,7 +4,7 @@ error: recursion limit reached while expanding `test!`
 LL | test!(test);
    | ^^^^^^^^^^^^
    |
-   = help: consider adding a `#![recursion_limit="0"]` attribute to your crate (`zero`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`zero`)
 
 error: aborting due to previous error
 
index 307bbcbd58d7a37fcc105eef5d7765abf64cdb9f..ad35936716806007b045b22d68a2933bf6f80750 100644 (file)
@@ -8,7 +8,7 @@ trait Foo {
 // Here, the object is bounded by an anonymous lifetime and returned
 // as `&'static`, so you get an error.
 fn owned_receiver(x: Box<dyn Foo>) -> &'static () {
-    x.borrowed() //~ ERROR cannot return value referencing local data `*x`
+    x.borrowed() //~ ERROR cannot return reference to local data `*x`
 }
 
 fn main() {}
index b86f6e3a2a12261860b853581cf7e3c149f8b973..b82b58c7a8e0d6c12e206a2c6dfc11fd9339a1a6 100644 (file)
@@ -1,11 +1,8 @@
-error[E0515]: cannot return value referencing local data `*x`
+error[E0515]: cannot return reference to local data `*x`
   --> $DIR/region-object-lifetime-5.rs:11:5
    |
 LL |     x.borrowed()
-   |     -^^^^^^^^^^^
-   |     |
-   |     returns a value referencing data owned by the current function
-   |     `*x` is borrowed here
+   |     ^^^^^^^^^^^^ returns a reference to data owned by the current function
 
 error: aborting due to previous error
 
index 008492529d18c9de7d6ce66ba46542402b287955..cad3ccc4a0ef836883eaa23f32a77506ab70f843 100644 (file)
@@ -16,10 +16,11 @@ error[E0425]: cannot find function, tuple struct or tuple variant `Bx` in this s
 LL |         Bx(());
    |         ^^ not found in this scope
    |
-help: consider importing this tuple struct
-   |
-LL |     use foo::Bx;
+note: tuple struct `foo::Bx` exists but is inaccessible
+  --> $DIR/issue-42944.rs:2:5
    |
+LL |     pub struct Bx(());
+   |     ^^^^^^^^^^^^^^^^^^ not accessible
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/resolve/issue-88472.rs b/src/test/ui/resolve/issue-88472.rs
new file mode 100644 (file)
index 0000000..6bf7cae
--- /dev/null
@@ -0,0 +1,38 @@
+// Regression test for #88472, where a suggestion was issued to
+// import an inaccessible struct.
+
+#![warn(unused_imports)]
+//~^ NOTE: the lint level is defined here
+
+mod a {
+    struct Foo;
+    //~^ NOTE: struct `a::Foo` exists but is inaccessible
+    //~| NOTE: not accessible
+}
+
+mod b {
+    use crate::a::*;
+    //~^ WARNING: unused import
+    type Bar = Foo;
+    //~^ ERROR: cannot find type `Foo` in this scope [E0412]
+    //~| NOTE: not found in this scope
+}
+
+mod c {
+    enum Eee {}
+    //~^ NOTE: these enums exist but are inaccessible
+    //~| NOTE: `c::Eee`: not accessible
+
+    mod d {
+        enum Eee {}
+        //~^ NOTE: `c::d::Eee`: not accessible
+    }
+}
+
+mod e {
+    type Baz = Eee;
+    //~^ ERROR: cannot find type `Eee` in this scope [E0412]
+    //~| NOTE: not found in this scope
+}
+
+fn main() {}
diff --git a/src/test/ui/resolve/issue-88472.stderr b/src/test/ui/resolve/issue-88472.stderr
new file mode 100644 (file)
index 0000000..8431fc9
--- /dev/null
@@ -0,0 +1,42 @@
+error[E0412]: cannot find type `Foo` in this scope
+  --> $DIR/issue-88472.rs:16:16
+   |
+LL |     type Bar = Foo;
+   |                ^^^ not found in this scope
+   |
+note: struct `a::Foo` exists but is inaccessible
+  --> $DIR/issue-88472.rs:8:5
+   |
+LL |     struct Foo;
+   |     ^^^^^^^^^^^ not accessible
+
+error[E0412]: cannot find type `Eee` in this scope
+  --> $DIR/issue-88472.rs:33:16
+   |
+LL |     type Baz = Eee;
+   |                ^^^ not found in this scope
+   |
+note: these enums exist but are inaccessible
+  --> $DIR/issue-88472.rs:22:5
+   |
+LL |     enum Eee {}
+   |     ^^^^^^^^ `c::Eee`: not accessible
+...
+LL |         enum Eee {}
+   |         ^^^^^^^^ `c::d::Eee`: not accessible
+
+warning: unused import: `crate::a::*`
+  --> $DIR/issue-88472.rs:14:9
+   |
+LL |     use crate::a::*;
+   |         ^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-88472.rs:4:9
+   |
+LL | #![warn(unused_imports)]
+   |         ^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0412`.
index 192349e0fafe31ab6b55c52dd7e11abd11ce9224..ff72b0b563ab176fc361e0b13a117f173fe00f8f 100644 (file)
@@ -169,16 +169,13 @@ LL |     pub enum E {
    |     ---------- similarly named enum `E` defined here
 ...
 LL |     let _: Z = m::n::Z;
-   |            ^
+   |            ^ help: an enum with a similar name exists: `E`
    |
-help: an enum with a similar name exists
-   |
-LL |     let _: E = m::n::Z;
-   |            ~
-help: consider importing this enum
-   |
-LL | use m::Z;
+note: enum `m::Z` exists but is inaccessible
+  --> $DIR/privacy-enum-ctor.rs:11:9
    |
+LL |         pub(in m) enum Z {
+   |         ^^^^^^^^^^^^^^^^ not accessible
 
 error[E0423]: expected value, found enum `m::n::Z`
   --> $DIR/privacy-enum-ctor.rs:57:16
@@ -215,16 +212,13 @@ LL |     pub enum E {
    |     ---------- similarly named enum `E` defined here
 ...
 LL |     let _: Z = m::n::Z::Fn;
-   |            ^
+   |            ^ help: an enum with a similar name exists: `E`
    |
-help: an enum with a similar name exists
-   |
-LL |     let _: E = m::n::Z::Fn;
-   |            ~
-help: consider importing this enum
-   |
-LL | use m::Z;
+note: enum `m::Z` exists but is inaccessible
+  --> $DIR/privacy-enum-ctor.rs:11:9
    |
+LL |         pub(in m) enum Z {
+   |         ^^^^^^^^^^^^^^^^ not accessible
 
 error[E0412]: cannot find type `Z` in this scope
   --> $DIR/privacy-enum-ctor.rs:64:12
@@ -233,16 +227,13 @@ LL |     pub enum E {
    |     ---------- similarly named enum `E` defined here
 ...
 LL |     let _: Z = m::n::Z::Struct;
-   |            ^
+   |            ^ help: an enum with a similar name exists: `E`
    |
-help: an enum with a similar name exists
-   |
-LL |     let _: E = m::n::Z::Struct;
-   |            ~
-help: consider importing this enum
-   |
-LL | use m::Z;
+note: enum `m::Z` exists but is inaccessible
+  --> $DIR/privacy-enum-ctor.rs:11:9
    |
+LL |         pub(in m) enum Z {
+   |         ^^^^^^^^^^^^^^^^ not accessible
 
 error[E0423]: expected value, found struct variant `m::n::Z::Struct`
   --> $DIR/privacy-enum-ctor.rs:64:16
@@ -262,16 +253,13 @@ LL |     pub enum E {
    |     ---------- similarly named enum `E` defined here
 ...
 LL |     let _: Z = m::n::Z::Unit {};
-   |            ^
+   |            ^ help: an enum with a similar name exists: `E`
    |
-help: an enum with a similar name exists
-   |
-LL |     let _: E = m::n::Z::Unit {};
-   |            ~
-help: consider importing this enum
-   |
-LL | use m::Z;
+note: enum `m::Z` exists but is inaccessible
+  --> $DIR/privacy-enum-ctor.rs:11:9
    |
+LL |         pub(in m) enum Z {
+   |         ^^^^^^^^^^^^^^^^ not accessible
 
 error[E0603]: enum `Z` is private
   --> $DIR/privacy-enum-ctor.rs:57:22
index e5d6f7e9e24f0627091909bfa437fd67acf17a42..ada053014ef5eb38e064fcf7c2c7c54ab94d6d09 100644 (file)
@@ -33,10 +33,11 @@ error[E0423]: expected value, found struct `xcrate::S`
 LL |     xcrate::S;
    |     ^^^^^^^^^ constructor is not visible here due to private fields
    |
-help: consider importing this tuple struct instead
-   |
-LL | use m::S;
+note: tuple struct `m::S` exists but is inaccessible
+  --> $DIR/privacy-struct-ctor.rs:6:5
    |
+LL |     pub struct S(u8);
+   |     ^^^^^^^^^^^^^^^^^ not accessible
 
 error[E0603]: tuple struct constructor `Z` is private
   --> $DIR/privacy-struct-ctor.rs:18:12
diff --git a/src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.nll.stderr b/src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.nll.stderr
deleted file mode 100644 (file)
index c74b82d..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable
-  --> $DIR/borrowck-issue-49631.rs:20:9
-   |
-LL |     while let Some(Ok(string)) = foo.get() {
-   |                                  --- immutable borrow occurs here
-LL |         foo.mutate();
-   |         ^^^ mutable borrow occurs here
-LL |
-LL |         println!("foo={:?}", *string);
-   |                              ------- immutable borrow later used here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0502`.
index 04572920ee414ad60b7f0e53e0bec5b3f2a6954a..b7c0b0bb6b93e8687a87c5c9ca17eb437eda65e9 100644 (file)
@@ -2,7 +2,7 @@ error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immu
   --> $DIR/borrowck-issue-49631.rs:20:9
    |
 LL |     while let Some(Ok(string)) = foo.get() {
-   |                                  --- immutable borrow occurs here
+   |                                  --------- immutable borrow occurs here
 LL |         foo.mutate();
    |         ^^^^^^^^^^^^ mutable borrow occurs here
 LL |
index cc247bbcb119900591bf063b80835d20690cd32d..7f15c1c1f57703473063f94fe6dfabd02498192f 100644 (file)
@@ -31,7 +31,7 @@ LL | |
 LL | | };
    | |_^ one type is more general than the other
    |
-   = note: expected type `for<'r, 's> Fn<(&'r Foo<'s>,)>`
+   = note: expected type `for<'a, 'b> Fn<(&'a Foo<'b>,)>`
               found type `Fn<(&Foo<'_>,)>`
 
 error[E0308]: mismatched types
@@ -46,7 +46,7 @@ LL | |
 LL | | };
    | |_^ one type is more general than the other
    |
-   = note: expected type `for<'r, 's> Fn<(&'r Foo<'s>,)>`
+   = note: expected type `for<'a, 'b> Fn<(&'a Foo<'b>,)>`
               found type `Fn<(&Foo<'_>,)>`
 
 error: implementation of `FnOnce` is not general enough
@@ -61,7 +61,7 @@ LL | |
 LL | | };
    | |_^ implementation of `FnOnce` is not general enough
    |
-   = note: `fn(&'2 Foo<'_>) -> &'2 Foo<'_> {id::<&'2 Foo<'_>>}` must implement `FnOnce<(&'1 Foo<'_>,)>`, for any lifetime `'1`...
+   = note: `fn(&'2 Foo<'_>) -> &'2 Foo<'_> {id::<&'2 Foo<'_>>}` must implement `FnOnce<(&'1 Foo<'b>,)>`, for any lifetime `'1`...
    = note: ...but it actually implements `FnOnce<(&'2 Foo<'_>,)>`, for some specific lifetime `'2`
 
 error: implementation of `FnOnce` is not general enough
@@ -76,7 +76,7 @@ LL | |
 LL | | };
    | |_^ implementation of `FnOnce` is not general enough
    |
-   = note: `fn(&Foo<'2>) -> &Foo<'2> {id::<&Foo<'2>>}` must implement `FnOnce<(&Foo<'1>,)>`, for any lifetime `'1`...
+   = note: `fn(&Foo<'2>) -> &Foo<'2> {id::<&Foo<'2>>}` must implement `FnOnce<(&'a Foo<'1>,)>`, for any lifetime `'1`...
    = note: ...but it actually implements `FnOnce<(&Foo<'2>,)>`, for some specific lifetime `'2`
 
 error: aborting due to 5 previous errors
index 47c04f1eb72ec08d1e6517706705dd95c1fc3304..aca08d81163fd7428a4b590705222f26b27fd500 100644 (file)
@@ -66,10 +66,11 @@ error[E0531]: cannot find unit struct, unit variant or constant `Self` in this s
 LL |         mut Self => (),
    |             ^^^^ not found in this scope
    |
-help: consider importing this unit struct
-   |
-LL | use foo::Self;
+note: unit struct `foo::Self` exists but is inaccessible
+  --> $DIR/self_type_keyword.rs:2:3
    |
+LL |   struct Self;
+   |   ^^^^^^^^^^^^ not accessible
 
 error[E0392]: parameter `'Self` is never used
   --> $DIR/self_type_keyword.rs:6:12
index 8fceef64c8cf26363ce758137d8305a98df3a5b0..e4ec9f875765a35d3f1c88dddfacb4dcf35c02f8 100644 (file)
@@ -56,7 +56,7 @@ error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
 LL | fn deref_mut_method1(x: Own<Point>) {
    |                      - help: consider changing this to be mutable: `mut x`
 LL |     x.set(0, 0);
-   |     ^ cannot borrow as mutable
+   |     ^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:121:5
@@ -64,7 +64,7 @@ error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
 LL | fn deref_extend_mut_method1(x: &Own<Point>) -> &mut isize {
    |                                ----------- help: consider changing this to be a mutable reference: `&mut Own<Point>`
 LL |     x.y_mut()
-   |     ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |     ^^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:129:6
@@ -72,7 +72,7 @@ error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
 LL | fn assign_method1<'a>(x: Own<Point>) {
    |                       - help: consider changing this to be mutable: `mut x`
 LL |     *x.y_mut() = 3;
-   |      ^ cannot borrow as mutable
+   |      ^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:133:6
@@ -80,7 +80,7 @@ error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
 LL | fn assign_method2<'a>(x: &'a Own<Point>) {
    |                          -------------- help: consider changing this to be a mutable reference: `&'a mut Own<Point>`
 LL |     *x.y_mut() = 3;
-   |      ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |      ^^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to 10 previous errors
 
index 0f630abd14876a5b21a0e00e40e1654270209403..b4693b7242ae42132263a66518d0bbb369de84d9 100644 (file)
@@ -24,7 +24,7 @@ error[E0596]: cannot borrow `f.f` as mutable, as it is behind a `&` reference
 LL | fn test4(f: &Test) {
    |             ----- help: consider changing this to be a mutable reference: `&mut Test<'_>`
 LL |     f.f.call_mut(())
-   |     ^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |     ^^^^^^^^^^^^^^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure
   --> $DIR/borrowck-call-is-borrow-issue-12224.rs:57:13
index 6b5e0779e5fa5cfed9c8f0171c4851c6daae5ba8..1864f5de108cd10911c221588cb43ea1ff85bf08 100644 (file)
@@ -5,7 +5,7 @@ LL | fn b(x: &Foo) {
    |         ---- help: consider changing this to be a mutable reference: `&mut Foo`
 LL |     x.f();
 LL |     x.h();
-   |     ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |     ^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
index 8949a10481a235cee02c8d86233a623643948f94..1f5d8bd32bb57faf27df0e2c8cdca481ca30d15a 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
 LL |     fn broken(x: &Vec<String>) {
    |                  ------------ help: consider changing this to be a mutable reference: `&mut Vec<String>`
 LL |         x.push(format!("this is broken"));
-   |         ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to previous error
 
index 7ba909d208aff467bd092bdfe120ef65c70f8286..7c5caba6eae32b607ddd83c3c74729643e88d54b 100644 (file)
@@ -32,7 +32,7 @@ LL |         v4.push(&id('y'));
    |                  creates a temporary which is freed while still in use
 ...
 LL |         v4.use_ref();
-   |         -- borrow later used here
+   |         ------------ borrow later used here
    |
    = note: consider using a `let` binding to create a longer lived value
 
index fe6d05c588f7ffbeeee60c823eb1126a88d3d7c3..cc43f6d0928dadb51e94e829793f837680a01b82 100644 (file)
@@ -5,7 +5,7 @@ LL | fn borrowed_receiver(x: &dyn Foo) {
    |                         -------- help: consider changing this to be a mutable reference: `&mut dyn Foo`
 LL |     x.borrowed();
 LL |     x.borrowed_mut();
-   |     ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |     ^^^^^^^^^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow `*x` as mutable, as `x` is not declared as mutable
   --> $DIR/borrowck-object-mutability.rs:18:5
@@ -14,7 +14,7 @@ LL | fn owned_receiver(x: Box<dyn Foo>) {
    |                   - help: consider changing this to be mutable: `mut x`
 LL |     x.borrowed();
 LL |     x.borrowed_mut();
-   |     ^ cannot borrow as mutable
+   |     ^^^^^^^^^^^^^^^^ cannot borrow as mutable
 
 error: aborting due to 2 previous errors
 
index 8f75c388f6576070b9ac1d54b6f8b6c55dbed24d..53c9404620f35d8633354a9eb9a113352852fe9d 100644 (file)
@@ -2,7 +2,7 @@ error[E0597]: `*a` does not live long enough
   --> $DIR/destructor-restrictions.rs:8:10
    |
 LL |         *a.borrow() + 1
-   |          ^---------
+   |          ^^^^^^^^^^
    |          |
    |          borrowed value does not live long enough
    |          a temporary with access to the borrow is created here ...
index 8d4709d660fa99d5a7f9e4404a00690b0a7520e4..3c2022748f0944339f99e3560083a49cd3cf1f7c 100644 (file)
@@ -2,7 +2,7 @@ error[E0597]: `y` does not live long enough
   --> $DIR/issue-23338-locals-die-before-temps-of-body.rs:10:5
    |
 LL |     y.borrow().clone()
-   |     ^---------
+   |     ^^^^^^^^^^
    |     |
    |     borrowed value does not live long enough
    |     a temporary with access to the borrow is created here ...
@@ -23,7 +23,7 @@ error[E0597]: `y` does not live long enough
   --> $DIR/issue-23338-locals-die-before-temps-of-body.rs:17:9
    |
 LL |         y.borrow().clone()
-   |         ^---------
+   |         ^^^^^^^^^^
    |         |
    |         borrowed value does not live long enough
    |         a temporary with access to the borrow is created here ...
index 0939584380af9c8ece730f97336edc5f502ae178..79a0ebaeb8db6c8b6ab999999737bd3194d2280e 100644 (file)
@@ -7,7 +7,7 @@ LL |         p = &a;
 LL |     }
    |     - `a` dropped here while still borrowed
 LL |     p.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error: aborting due to previous error
 
index 0b365c3f7b6b35d07db58366a5436eb8d65569b4..57f80214a4f83ad2c9988fda6677fdf434aaea07 100644 (file)
@@ -2,7 +2,7 @@ error[E0597]: `foo` does not live long enough
   --> $DIR/issue-40157.rs:2:53
    |
 LL |     {println!("{:?}", match { let foo = vec![1, 2]; foo.get(1) } { x => x });}
-   |                             ------------------------^^^---------
+   |                             ------------------------^^^^^^^^^^--
    |                             |                       |          |
    |                             |                       |          `foo` dropped here while still borrowed
    |                             |                       borrowed value does not live long enough
index 288c1042a26c72be5bca08a6284bc23a22b0337e..783f5aca4174681a49181bed9bc80f0f35c32bab 100644 (file)
@@ -27,16 +27,16 @@ LL |     fn f9(_: usize) -> usize;
            candidate #3: `UnusedTrait`
 help: disambiguate the associated function for candidate #1
    |
-LL |     u.f8(42) + CtxtFn::f9(u, 342) + m.fff(42)
-   |                ~~~~~~~~~~~~~~~~~~
+LL |     u.f8(42) + <usize as CtxtFn>::f9(u, 342) + m.fff(42)
+   |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 help: disambiguate the associated function for candidate #2
    |
-LL |     u.f8(42) + OtherTrait::f9(u, 342) + m.fff(42)
-   |                ~~~~~~~~~~~~~~~~~~~~~~
+LL |     u.f8(42) + <usize as OtherTrait>::f9(u, 342) + m.fff(42)
+   |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 help: disambiguate the associated function for candidate #3
    |
-LL |     u.f8(42) + UnusedTrait::f9(u, 342) + m.fff(42)
-   |                ~~~~~~~~~~~~~~~~~~~~~~~
+LL |     u.f8(42) + <usize as UnusedTrait>::f9(u, 342) + m.fff(42)
+   |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0599]: no method named `fff` found for struct `Myisize` in the current scope
   --> $DIR/issue-7575.rs:62:30
@@ -72,7 +72,7 @@ LL |     fn is_str() -> bool {
    = help: items from traits can only be used if the type parameter is bounded by the trait
 help: disambiguate the associated function for the candidate
    |
-LL |     ManyImplTrait::is_str(t)
+LL |     <T as ManyImplTrait>::is_str(t)
    |
 
 error: aborting due to 3 previous errors
index e04e4cbdabbf6c7724679ae160de8be8c2f9e3f1..8a7c504f0053bdaa94a9ba5ebcc036166d87f189 100644 (file)
@@ -4,7 +4,7 @@ error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
 LL |     fn foo(mut a: &String) {
    |                   ------- help: consider changing this to be a mutable reference: `&mut String`
 LL |         a.push_str("bar");
-   |         ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |         ^^^^^^^^^^^^^^^^^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
   --> $DIR/mut-arg-hint.rs:8:5
@@ -12,7 +12,7 @@ error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
 LL | pub fn foo<'a>(mut a: &'a String) {
    |                       ---------- help: consider changing this to be a mutable reference: `&'a mut String`
 LL |     a.push_str("foo");
-   |     ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |     ^^^^^^^^^^^^^^^^^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
   --> $DIR/mut-arg-hint.rs:15:9
@@ -20,7 +20,7 @@ error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
 LL |     pub fn foo(mut a: &String) {
    |                       ------- help: consider changing this to be a mutable reference: `&mut String`
 LL |         a.push_str("foo");
-   |         ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+   |         ^^^^^^^^^^^^^^^^^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
 error: aborting due to 3 previous errors
 
index 21b29464df50297c5c4bd920a676e9bcd427d3aa..4d976a7bbfa47bb52b59dd675606d3a49273a86b 100644 (file)
@@ -7,7 +7,7 @@ LL |     }
    |     - `b` dropped here while still borrowed
 LL |
 LL |     p.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error: aborting due to previous error
 
index 8e9cd595154438369e172b1f7252beaa03813e06..0b985de609c26debdb98dbdcca279a3a685f655f 100644 (file)
@@ -7,7 +7,7 @@ LL |         let c_ref = &c;
 LL |     }
    |     - `c` dropped here while still borrowed
 LL |     f.use_mut();
-   |     - borrow later used here
+   |     ----------- borrow later used here
 
 error: aborting due to previous error
 
index b47250db723fecf8e74b19e7b7a2d529e3dfeb0c..2b649307739f5c89b08ced82852cc6a4b03ce652 100644 (file)
@@ -7,7 +7,7 @@ LL |     while x < 10 {
    |           ^ use of borrowed `x`
 LL |         let mut z = x;
 LL |         _y.push(&mut z);
-   |         -- borrow later used here
+   |         --------------- borrow later used here
 
 error[E0503]: cannot use `x` because it was mutably borrowed
   --> $DIR/regions-escape-loop-via-vec.rs:6:21
@@ -18,14 +18,15 @@ LL |     while x < 10 {
 LL |         let mut z = x;
    |                     ^ use of borrowed `x`
 LL |         _y.push(&mut z);
-   |         -- borrow later used here
+   |         --------------- borrow later used here
 
 error[E0597]: `z` does not live long enough
   --> $DIR/regions-escape-loop-via-vec.rs:7:17
    |
 LL |         _y.push(&mut z);
-   |         --      ^^^^^^ borrowed value does not live long enough
-   |         |
+   |         --------^^^^^^-
+   |         |       |
+   |         |       borrowed value does not live long enough
    |         borrow later used here
 ...
 LL |     }
@@ -38,7 +39,7 @@ LL |     let mut _y = vec![&mut x];
    |                       ------ borrow of `x` occurs here
 ...
 LL |         _y.push(&mut z);
-   |         -- borrow later used here
+   |         --------------- borrow later used here
 LL |
 LL |         x += 1;
    |         ^^^^^^ use of borrowed `x`
index 81de8c29906f7a3a43b105dd14a65f5c650dc254..5d493a3e4ee5555cfb2dd559196e8e1b11e1c5ca 100644 (file)
@@ -7,7 +7,7 @@ LL |     drop(y);
    |          ^ move out of `y` occurs here
 ...
 LL |         *lock.lock().unwrap() = &z;
-   |          ---- borrow later used here
+   |          ----------- borrow later used here
 
 error[E0597]: `z` does not live long enough
   --> $DIR/send-is-not-static-std-sync.rs:16:33
@@ -18,7 +18,7 @@ LL |     }
    |     - `z` dropped here while still borrowed
 LL |
 LL |     lock.use_ref(); // (Mutex is #[may_dangle] so its dtor does not use `z` => needs explicit use)
-   |     ---- borrow later used here
+   |     -------------- borrow later used here
 
 error[E0505]: cannot move out of `y` because it is borrowed
   --> $DIR/send-is-not-static-std-sync.rs:27:10
@@ -29,7 +29,7 @@ LL |     drop(y);
    |          ^ move out of `y` occurs here
 ...
 LL |         *lock.write().unwrap() = &z;
-   |          ---- borrow later used here
+   |          ------------ borrow later used here
 
 error[E0597]: `z` does not live long enough
   --> $DIR/send-is-not-static-std-sync.rs:30:34
@@ -40,7 +40,7 @@ LL |     }
    |     - `z` dropped here while still borrowed
 LL |
 LL |     lock.use_ref(); // (RwLock is #[may_dangle] so its dtor does not use `z` => needs explicit use)
-   |     ---- borrow later used here
+   |     -------------- borrow later used here
 
 error[E0505]: cannot move out of `y` because it is borrowed
   --> $DIR/send-is-not-static-std-sync.rs:43:10
@@ -51,7 +51,7 @@ LL |     drop(y);
    |          ^ move out of `y` occurs here
 ...
 LL |         tx.send(&z).unwrap();
-   |         -- borrow later used here
+   |         ----------- borrow later used here
 
 error[E0597]: `z` does not live long enough
   --> $DIR/send-is-not-static-std-sync.rs:46:17
index 62a4a6009d433780516a3ff835ca8712b89e6704..27df25be3fa03580a44316b222127c7ac1efdcf1 100644 (file)
@@ -7,7 +7,7 @@ LL |         let x: &[isize] = &vec![1, 2, 3, 4, 5];
 LL |     }
    |     - temporary value is freed at the end of this statement
 LL |     y.use_ref();
-   |     - borrow later used here
+   |     ----------- borrow later used here
    |
    = note: consider using a `let` binding to create a longer lived value
    = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/src/test/ui/suggestions/core-std-import-order-issue-83564.rs b/src/test/ui/suggestions/core-std-import-order-issue-83564.rs
new file mode 100644 (file)
index 0000000..b7fe5af
--- /dev/null
@@ -0,0 +1,10 @@
+// edition:2018
+
+// This is a regression test for #83564.
+// For some reason, Rust 2018 or higher is required to reproduce the bug.
+
+fn main() {
+    //~^ HELP consider importing one of these items
+    let _x = NonZeroU32::new(5).unwrap();
+    //~^ ERROR failed to resolve: use of undeclared type `NonZeroU32`
+}
diff --git a/src/test/ui/suggestions/core-std-import-order-issue-83564.stderr b/src/test/ui/suggestions/core-std-import-order-issue-83564.stderr
new file mode 100644 (file)
index 0000000..d484fb8
--- /dev/null
@@ -0,0 +1,16 @@
+error[E0433]: failed to resolve: use of undeclared type `NonZeroU32`
+  --> $DIR/core-std-import-order-issue-83564.rs:8:14
+   |
+LL |     let _x = NonZeroU32::new(5).unwrap();
+   |              ^^^^^^^^^^ not found in this scope
+   |
+help: consider importing one of these items
+   |
+LL | use std::num::NonZeroU32;
+   |
+LL | use core::num::NonZeroU32;
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0433`.
index 043a771129a617a45a0d86c66ea5f8c9321d4269..80d3c940eb7c618c784dd83f400c575eedb12eac 100644 (file)
@@ -4,7 +4,7 @@ error[E0597]: `val` does not live long enough
 LL |     fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> {
    |               -- lifetime `'a` defined here                  ------------------- opaque type requires that `val` is borrowed for `'a`
 LL |         val.use_self()
-   |         ^^^ borrowed value does not live long enough
+   |         ^^^^^^^^^^^^^^ borrowed value does not live long enough
 LL |     }
    |     - `val` dropped here while still borrowed
    |
@@ -13,23 +13,17 @@ help: you can add a bound to the opaque type to make it last less than `'static`
 LL |     fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> + 'a {
    |                                                                                  ++++
 
-error[E0515]: cannot return value referencing function parameter `val`
+error[E0515]: cannot return reference to function parameter `val`
   --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:43:9
    |
 LL |         val.use_self()
-   |         ---^^^^^^^^^^^
-   |         |
-   |         returns a value referencing data owned by the current function
-   |         `val` is borrowed here
+   |         ^^^^^^^^^^^^^^ returns a reference to data owned by the current function
 
-error[E0515]: cannot return value referencing function parameter `val`
+error[E0515]: cannot return reference to function parameter `val`
   --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:109:9
    |
 LL |         val.use_self()
-   |         ---^^^^^^^^^^^
-   |         |
-   |         returns a value referencing data owned by the current function
-   |         `val` is borrowed here
+   |         ^^^^^^^^^^^^^^ returns a reference to data owned by the current function
 
 error: aborting due to 3 previous errors
 
index 29991b6572fb42b413b57743f53730045e4b4e8e..e8c3a7908f5211dc3bb2c26190b7af105bdecd27 100644 (file)
@@ -4,7 +4,7 @@ error[E0597]: `val` does not live long enough
 LL |     fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> {
    |               -- lifetime `'a` defined here                  ------------------- opaque type requires that `val` is borrowed for `'a`
 LL |         val.use_self()
-   |         ^^^ borrowed value does not live long enough
+   |         ^^^^^^^^^^^^^^ borrowed value does not live long enough
 LL |     }
    |     - `val` dropped here while still borrowed
    |
@@ -13,23 +13,17 @@ help: you can add a bound to the opaque type to make it last less than `'static`
 LL |     fn use_it<'a>(val: Box<dyn ObjectTrait<Assoc = i32>>) -> impl OtherTrait<'a> + 'a {
    |                                                                                  ++++
 
-error[E0515]: cannot return value referencing function parameter `val`
+error[E0515]: cannot return reference to function parameter `val`
   --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:43:9
    |
 LL |         val.use_self()
-   |         ---^^^^^^^^^^^
-   |         |
-   |         returns a value referencing data owned by the current function
-   |         `val` is borrowed here
+   |         ^^^^^^^^^^^^^^ returns a reference to data owned by the current function
 
-error[E0515]: cannot return value referencing function parameter `val`
+error[E0515]: cannot return reference to function parameter `val`
   --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:109:9
    |
 LL |         val.use_self()
-   |         ---^^^^^^^^^^^
-   |         |
-   |         returns a value referencing data owned by the current function
-   |         `val` is borrowed here
+   |         ^^^^^^^^^^^^^^ returns a reference to data owned by the current function
 
 error[E0772]: `val` has lifetime `'a` but calling `use_self` introduces an implicit `'static` lifetime requirement
   --> $DIR/impl-on-dyn-trait-with-implicit-static-bound-needing-more-suggestions.rs:66:13
diff --git a/src/test/ui/suggestions/issue-88730.rs b/src/test/ui/suggestions/issue-88730.rs
new file mode 100644 (file)
index 0000000..e63210a
--- /dev/null
@@ -0,0 +1,16 @@
+#![allow(unused, nonstandard_style)]
+#![deny(bindings_with_variant_name)]
+
+// If an enum has two different variants,
+// then it cannot be matched upon in a function argument.
+// It still gets a warning, but no suggestions.
+enum Foo {
+    C,
+    D,
+}
+
+fn foo(C: Foo) {} //~ERROR
+
+fn main() {
+    let C = Foo::D; //~ERROR
+}
diff --git a/src/test/ui/suggestions/issue-88730.stderr b/src/test/ui/suggestions/issue-88730.stderr
new file mode 100644 (file)
index 0000000..eb22b0e
--- /dev/null
@@ -0,0 +1,21 @@
+error[E0170]: pattern binding `C` is named the same as one of the variants of the type `Foo`
+  --> $DIR/issue-88730.rs:12:8
+   |
+LL | fn foo(C: Foo) {}
+   |        ^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-88730.rs:2:9
+   |
+LL | #![deny(bindings_with_variant_name)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0170]: pattern binding `C` is named the same as one of the variants of the type `Foo`
+  --> $DIR/issue-88730.rs:15:9
+   |
+LL |     let C = Foo::D;
+   |         ^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0170`.
diff --git a/src/test/ui/suggestions/negative-literal-index.fixed b/src/test/ui/suggestions/negative-literal-index.fixed
new file mode 100644 (file)
index 0000000..e52714c
--- /dev/null
@@ -0,0 +1,22 @@
+// run-rustfix
+
+use std::ops::Index;
+struct X;
+impl Index<i32> for X {
+    type Output = ();
+
+    fn index(&self, _: i32) -> &() {
+        &()
+    }
+}
+
+fn main() {
+    let x = vec![1, 2, 3];
+    x[x.len() -1]; //~ ERROR negative integers cannot be used to index on a
+    let x = [1, 2, 3];
+    x[x.len() -1]; //~ ERROR negative integers cannot be used to index on a
+    let x = &[1, 2, 3];
+    x[x.len() -1]; //~ ERROR negative integers cannot be used to index on a
+    let _ = x;
+    X[-1];
+}
diff --git a/src/test/ui/suggestions/negative-literal-index.rs b/src/test/ui/suggestions/negative-literal-index.rs
new file mode 100644 (file)
index 0000000..d88b66e
--- /dev/null
@@ -0,0 +1,22 @@
+// run-rustfix
+
+use std::ops::Index;
+struct X;
+impl Index<i32> for X {
+    type Output = ();
+
+    fn index(&self, _: i32) -> &() {
+        &()
+    }
+}
+
+fn main() {
+    let x = vec![1, 2, 3];
+    x[-1]; //~ ERROR negative integers cannot be used to index on a
+    let x = [1, 2, 3];
+    x[-1]; //~ ERROR negative integers cannot be used to index on a
+    let x = &[1, 2, 3];
+    x[-1]; //~ ERROR negative integers cannot be used to index on a
+    let _ = x;
+    X[-1];
+}
diff --git a/src/test/ui/suggestions/negative-literal-index.stderr b/src/test/ui/suggestions/negative-literal-index.stderr
new file mode 100644 (file)
index 0000000..2b51bf7
--- /dev/null
@@ -0,0 +1,35 @@
+error: negative integers cannot be used to index on a `Vec<{integer}>`
+  --> $DIR/negative-literal-index.rs:15:7
+   |
+LL |     x[-1];
+   |       ^^ cannot use a negative integer for indexing on `Vec<{integer}>`
+   |
+help: to access an element starting from the end of the `Vec<{integer}>`, compute the index
+   |
+LL |     x[x.len() -1];
+   |       +++++++
+
+error: negative integers cannot be used to index on a `[{integer}; 3]`
+  --> $DIR/negative-literal-index.rs:17:7
+   |
+LL |     x[-1];
+   |       ^^ cannot use a negative integer for indexing on `[{integer}; 3]`
+   |
+help: to access an element starting from the end of the `[{integer}; 3]`, compute the index
+   |
+LL |     x[x.len() -1];
+   |       +++++++
+
+error: negative integers cannot be used to index on a `[{integer}; 3]`
+  --> $DIR/negative-literal-index.rs:19:7
+   |
+LL |     x[-1];
+   |       ^^ cannot use a negative integer for indexing on `[{integer}; 3]`
+   |
+help: to access an element starting from the end of the `[{integer}; 3]`, compute the index
+   |
+LL |     x[x.len() -1];
+   |       +++++++
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/suggestions/suggest-trait-items.rs b/src/test/ui/suggestions/suggest-trait-items.rs
new file mode 100644 (file)
index 0000000..9d42a73
--- /dev/null
@@ -0,0 +1,48 @@
+trait Foo {
+    type Type;
+
+    fn foo();
+    fn bar();
+    fn qux();
+}
+
+struct A;
+
+impl Foo for A {
+//~^ ERROR not all trait items implemented
+    type Typ = ();
+    //~^ ERROR type `Typ` is not a member of trait
+    //~| HELP there is an associated type with a similar name
+
+    fn fooo() {}
+    //~^ ERROR method `fooo` is not a member of trait
+    //~| HELP there is an associated function with a similar name
+
+    fn barr() {}
+    //~^ ERROR method `barr` is not a member of trait
+    //~| HELP there is an associated function with a similar name
+
+    fn quux() {}
+    //~^ ERROR method `quux` is not a member of trait
+    //~| HELP there is an associated function with a similar name
+}
+//~^ HELP implement the missing item
+//~| HELP implement the missing item
+//~| HELP implement the missing item
+//~| HELP implement the missing item
+
+trait Bar {
+    const Const: i32;
+}
+
+struct B;
+
+impl Bar for B {
+//~^ ERROR not all trait items implemented
+    const Cnst: i32 = 0;
+    //~^ ERROR const `Cnst` is not a member of trait
+    //~| HELP there is an associated constant with a similar name
+}
+//~^ HELP implement the missing item
+
+fn main() {}
diff --git a/src/test/ui/suggestions/suggest-trait-items.stderr b/src/test/ui/suggestions/suggest-trait-items.stderr
new file mode 100644 (file)
index 0000000..151bae7
--- /dev/null
@@ -0,0 +1,74 @@
+error[E0437]: type `Typ` is not a member of trait `Foo`
+  --> $DIR/suggest-trait-items.rs:13:5
+   |
+LL |     type Typ = ();
+   |     ^^^^^---^^^^^^
+   |     |    |
+   |     |    help: there is an associated type with a similar name: `Type`
+   |     not a member of trait `Foo`
+
+error[E0407]: method `fooo` is not a member of trait `Foo`
+  --> $DIR/suggest-trait-items.rs:17:5
+   |
+LL |     fn fooo() {}
+   |     ^^^----^^^^^
+   |     |  |
+   |     |  help: there is an associated function with a similar name: `foo`
+   |     not a member of trait `Foo`
+
+error[E0407]: method `barr` is not a member of trait `Foo`
+  --> $DIR/suggest-trait-items.rs:21:5
+   |
+LL |     fn barr() {}
+   |     ^^^----^^^^^
+   |     |  |
+   |     |  help: there is an associated function with a similar name: `bar`
+   |     not a member of trait `Foo`
+
+error[E0407]: method `quux` is not a member of trait `Foo`
+  --> $DIR/suggest-trait-items.rs:25:5
+   |
+LL |     fn quux() {}
+   |     ^^^----^^^^^
+   |     |  |
+   |     |  help: there is an associated function with a similar name: `qux`
+   |     not a member of trait `Foo`
+
+error[E0438]: const `Cnst` is not a member of trait `Bar`
+  --> $DIR/suggest-trait-items.rs:42:5
+   |
+LL |     const Cnst: i32 = 0;
+   |     ^^^^^^----^^^^^^^^^^
+   |     |     |
+   |     |     help: there is an associated constant with a similar name: `Const`
+   |     not a member of trait `Bar`
+
+error[E0046]: not all trait items implemented, missing: `Type`, `foo`, `bar`, `qux`
+  --> $DIR/suggest-trait-items.rs:11:1
+   |
+LL |     type Type;
+   |     ---------- `Type` from trait
+LL | 
+LL |     fn foo();
+   |     --------- `foo` from trait
+LL |     fn bar();
+   |     --------- `bar` from trait
+LL |     fn qux();
+   |     --------- `qux` from trait
+...
+LL | impl Foo for A {
+   | ^^^^^^^^^^^^^^ missing `Type`, `foo`, `bar`, `qux` in implementation
+
+error[E0046]: not all trait items implemented, missing: `Const`
+  --> $DIR/suggest-trait-items.rs:40:1
+   |
+LL |     const Const: i32;
+   |     ----------------- `Const` from trait
+...
+LL | impl Bar for B {
+   | ^^^^^^^^^^^^^^ missing `Const` in implementation
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0046, E0407, E0437, E0438.
+For more information about an error, try `rustc --explain E0046`.
index d2ac3a836f62261a6f9c0fe1eed0d1dfbbf6f3d2..920f66121e09897094a55787cdcd975c1105cde1 100644 (file)
@@ -4,7 +4,7 @@ error[E0275]: overflow evaluating the requirement `Option<_>: Sized`
 LL |     iso(left, right)
    |     ^^^
    |
-   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`mutual_recursion_issue_75860`)
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`mutual_recursion_issue_75860`)
 note: required by a bound in `Option`
   --> $SRC_DIR/core/src/option.rs:LL:COL
    |
diff --git a/src/test/ui/typeck/call-block.rs b/src/test/ui/typeck/call-block.rs
new file mode 100644 (file)
index 0000000..0390d7d
--- /dev/null
@@ -0,0 +1,3 @@
+fn main() {
+    let _ = {42}(); //~ ERROR expected function, found `{integer}`
+}
diff --git a/src/test/ui/typeck/call-block.stderr b/src/test/ui/typeck/call-block.stderr
new file mode 100644 (file)
index 0000000..68984bc
--- /dev/null
@@ -0,0 +1,11 @@
+error[E0618]: expected function, found `{integer}`
+  --> $DIR/call-block.rs:2:13
+   |
+LL |     let _ = {42}();
+   |             ^^^^--
+   |             |
+   |             call expression requires function
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0618`.
index 95a0f4f5fabee8a6aa07996351d60eb90e5606c6..199d5e3727850961ac894dc2d68a0a9ea893ecd6 100644 (file)
@@ -7,7 +7,7 @@ LL |     !x;
    |     -- `x` moved due to usage in operator
 LL | 
 LL |     x.clone();
-   |     ^ value borrowed here after move
+   |     ^^^^^^^^^ value borrowed here after move
    |
 note: calling this operator moves the left-hand side
   --> $SRC_DIR/core/src/ops/bit.rs:LL:COL
index 539ea94a70de54effe3977b6390ba96ae1da869f..0ad6d6c7c0e075f25d233c832a32be374588fd79 100644 (file)
@@ -8,7 +8,7 @@ LL |     l.push(n);
    |            - value moved here
 LL | 
 LL |     let x = n.to_string();
-   |             ^ value borrowed here after move
+   |             ^^^^^^^^^^^^^ value borrowed here after move
 
 error: aborting due to previous error
 
index 0121d66aa2ef5ffa9735f86c2b56f5fdc5a837a6..d56b42c549dbb7e7d0f712c51b39400260d114d4 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 0121d66aa2ef5ffa9735f86c2b56f5fdc5a837a6
+Subproject commit d56b42c549dbb7e7d0f712c51b39400260d114d4
index 84ae36a46d71de51a394f27bc3fe2533c2ccec60..688473f2f9bfcee25d3d1f7484967cbcd1ec3154 100644 (file)
@@ -1,9 +1,10 @@
 [alias]
 uitest = "test --test compile-test"
-dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
-lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml  -- "
+dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
+lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml  -- "
 collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored"
 
 [build]
 # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
 rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"]
+target-dir = "target"
index 2891d5e5da1e1ec7490817e6089a867ad1c47ff4..866303a1f9fd58d3a7e653237843ecc5dbd8db49 100644 (file)
@@ -8,7 +8,7 @@ about: Create a blank issue.
 Additional labels can be added to this issue by including the following command
 (without the space after the @ symbol):
 
-`@rustbot label +<label>`
+@ rustbot label +<label>
 
 Common labels for this issue type are:
 * C-an-interesting-project
index 87c18cdee66c5d24d96ddf95aafe305a9b2beaf7..119a498fb99eb9fdee894f2d08b853701e5b60a0 100644 (file)
@@ -36,7 +36,7 @@ LLVM version: 10.0
 Additional labels can be added to this issue by including the following command
 (without the space after the @ symbol):
 
-`@rustbot label +<label>`
+@ rustbot label +<label>
 
 Common labels for this issue type are:
 * `I-suggestion-causes-error`
index 4170b9ff2dbe5d149854fa8c2167efc4fec45a52..82158e02f085eacc65c248fb3e86e68a24f7aad7 100644 (file)
@@ -37,7 +37,7 @@ LLVM version: 10.0
 Additional labels can be added to this issue by including the following command
 (without the space after the @ symbol):
 
-`@rustbot label +<label>`
+@ rustbot label +<label>
 
 Common labels for this issue type are:
 * I-suggestion-causes-error
index f5ac2f7c9f8c92a596aeb79401441bb8eaf4c3b3..58ea0f9ab9d286f6229db205a99a7983c7d43ba0 100644 (file)
@@ -6,11 +6,81 @@ document.
 
 ## Unreleased / In Rust Nightly
 
-[74d1561...master](https://github.com/rust-lang/rust-clippy/compare/74d1561...master)
+[7bfc26e...master](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...master)
+
+## Rust 1.56
+
+Current beta, release 2021-10-21
+
+[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
+
+### New Lints
+
+* [`unwrap_or_else_default`]
+  [#7516](https://github.com/rust-lang/rust-clippy/pull/7516)
+
+### Enhancements
+
+* [`needless_continue`]: Now also lints in `loop { continue; }` case
+  [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
+* [`disallowed_type`]: Now also primitive types can be disallowed
+  [#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
+* [`manual_swap`]: Now also lints on xor swaps
+  [#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
+* [`map_flatten`]: Now also lints on the `Result` type
+  [#7522](https://github.com/rust-lang/rust-clippy/pull/7522)
+* [`no_effect`]: Now also lints on inclusive ranges
+  [#7556](https://github.com/rust-lang/rust-clippy/pull/7556)
+
+### False Positive Fixes
+
+* [`nonstandard_macro_braces`]: No longer lints on similar named nested macros
+  [#7478](https://github.com/rust-lang/rust-clippy/pull/7478)
+* [`too_many_lines`]: No longer lints in closures to avoid duplicated diagnostics
+  [#7534](https://github.com/rust-lang/rust-clippy/pull/7534)
+* [`similar_names`]: No longer complains about `iter` and `item` being too
+  similar [#7546](https://github.com/rust-lang/rust-clippy/pull/7546)
+
+### Suggestion Fixes/Improvements
+
+* [`similar_names`]: No longer suggests to insert or add an underscore as a fix
+  [#7221](https://github.com/rust-lang/rust-clippy/pull/7221)
+* [`new_without_default`]: No longer shows the full qualified type path when
+  suggesting adding a `Default` implementation
+  [#7493](https://github.com/rust-lang/rust-clippy/pull/7493)
+* [`while_let_on_iterator`]: Now suggests re-borrowing mutable references
+  [#7520](https://github.com/rust-lang/rust-clippy/pull/7520)
+* [`extend_with_drain`]: Improve code suggestion for mutable and immutable
+  references [#7533](https://github.com/rust-lang/rust-clippy/pull/7533)
+* [`trivially_copy_pass_by_ref`]: Now properly handles `Self` type
+  [#7535](https://github.com/rust-lang/rust-clippy/pull/7535)
+* [`never_loop`]: Now suggests using `if let` instead of a `for` loop when
+  applicable [#7541](https://github.com/rust-lang/rust-clippy/pull/7541)
+
+### Documentation Improvements
+
+* Clippy now uses a lint to generate its lint documentation. [Lints all the way
+  down](https://en.wikipedia.org/wiki/Turtles_all_the_way_down).
+  [#7502](https://github.com/rust-lang/rust-clippy/pull/7502)
+* Reworked Clippy's website:
+  [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
+  [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
+  * Added applicability information about lints
+  * Added a link to jump into the implementation
+  * Improved loading times
+  * Adapted some styling
+* `cargo clippy --help` now also explains the `--fix` and `--no-deps` flag
+  [#7492](https://github.com/rust-lang/rust-clippy/pull/7492)
+* [`unnested_or_patterns`]: Removed `or_patterns` feature gate in the code
+  example [#7507](https://github.com/rust-lang/rust-clippy/pull/7507)
+
+### New Lints
+
+* Renamed Lint: `if_let_some_result` is now called [`match_result_ok`]. Now also handles `while let` case.
 
 ## Rust 1.55
 
-Current beta, release 2021-09-09
+Current stable, released 2021-09-09
 
 [3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
 
@@ -126,21 +196,9 @@ Current beta, release 2021-09-09
 * [`use_self`]
   [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
 
-### Documentation Improvements
-
-* Reworked Clippy's website:
-  [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
-  [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
-  * Added applicability information about lints
-  * Added a link to jump into the implementation
-  * Improved loading times
-  * Adapted some styling
-* Clippy now uses a lint to generate its documentation
-  [#7298](https://github.com/rust-lang/rust-clippy/pull/7298)
-
 ## Rust 1.54
 
-Current stable, released 2021-07-29
+Released 2021-07-29
 
 [7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
 
@@ -1050,7 +1108,7 @@ Released 2020-11-19
   [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
 * Add example of false positive to [`ptr_arg`] docs.
   [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
-* [`box_vec`], [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
+* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
   [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
 
 ## Rust 1.47
@@ -1491,7 +1549,7 @@ Released 2020-03-12
 * `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 * [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 * [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
-* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
+* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
 
 ### ICE fixes
 
@@ -2570,7 +2628,7 @@ Released 2018-09-13
 [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
 [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
-[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
+[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
 [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
 [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
@@ -2685,9 +2743,9 @@ Released 2018-09-13
 [`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
 [`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
 [`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
-[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
 [`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
 [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
+[`if_then_panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_panic
 [`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
 [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
 [`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
@@ -2722,6 +2780,7 @@ Released 2018-09-13
 [`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
 [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
 [`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
+[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
 [`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
 [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
 [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
@@ -2773,6 +2832,7 @@ Released 2018-09-13
 [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
 [`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
 [`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
+[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
 [`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
 [`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
 [`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
@@ -2905,6 +2965,7 @@ Released 2018-09-13
 [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
 [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
 [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
+[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
 [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
 [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
 [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
index 40aaa5924df5b391862f379c8fd159920f5b061d..ba3ed3053ac743466be07b2fdf04c97911dcc0f1 100644 (file)
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
 keywords = ["clippy", "lint", "plugin"]
 categories = ["development-tools", "development-tools::cargo-plugins"]
 build = "build.rs"
-edition = "2018"
+edition = "2021"
 publish = false
 
 [[bin]]
index aaf404eadea11e9ac52730c83a8331fa1ded4e4e..822335fc65f13f1616203bb5b0a989efa80c5154 100644 (file)
@@ -18,7 +18,7 @@ You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the
 | `clippy::style`       | code that should be written in a more idiomatic way                                 | **warn**      |
 | `clippy::complexity`  | code that does something simple but in a complex way                                | **warn**      |
 | `clippy::perf`        | code that can be written to run faster                                              | **warn**      |
-| `clippy::pedantic`    | lints which are rather strict or might have false positives                         | allow         |
+| `clippy::pedantic`    | lints which are rather strict or have occasional false positives                    | allow         |
 | `clippy::nursery`     | new lints that are still under development                                          | allow         |
 | `clippy::cargo`       | lints for the cargo manifest                                                        | allow         |
 
index d7d2655026b4bc941ea2d54bf219bc173e091083..4a13a4524097601ffda40ce63349ca71fef9563e 100644 (file)
@@ -1,7 +1,7 @@
 [package]
 name = "clippy_dev"
 version = "0.0.1"
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 bytecount = "0.6"
index c4fa0a9aca70b75333b37f5fe5f6e70decdbe3cf..daf0fcc993bad320ff894014596fa13328ba0307 100644 (file)
@@ -1,7 +1,6 @@
 //! `bless` updates the reference files in the repo with changed output files
 //! from the last test run.
 
-use std::env;
 use std::ffi::OsStr;
 use std::fs;
 use std::lazy::SyncLazy;
@@ -10,17 +9,9 @@
 
 use crate::clippy_project_root;
 
-// NOTE: this is duplicated with tests/cargo/mod.rs What to do?
-pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
-    Some(v) => v.into(),
-    None => env::current_dir().unwrap().join("target"),
-});
-
 static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> = SyncLazy::new(|| {
-    let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
-    let mut path = PathBuf::from(&**CARGO_TARGET_DIR);
-    path.push(profile);
-    path.push("cargo-clippy");
+    let mut path = std::env::current_exe().unwrap();
+    path.set_file_name("cargo-clippy");
     fs::metadata(path).ok()?.modified().ok()
 });
 
@@ -94,10 +85,7 @@ fn updated_since_clippy_build(path: &Path) -> Option<bool> {
 }
 
 fn build_dir() -> PathBuf {
-    let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
-    let mut path = PathBuf::new();
-    path.push(CARGO_TARGET_DIR.clone());
-    path.push(profile);
-    path.push("test_build_base");
+    let mut path = std::env::current_exe().unwrap();
+    path.set_file_name("test");
     path
 }
index e59175a55e184e9eaedfaecd76c79580c4d55383..7900dc6d0414136a0ef27a74f787c7d97cd6fc05 100644 (file)
@@ -6,7 +6,7 @@ repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
 license = "MIT OR Apache-2.0"
 keywords = ["clippy", "lint", "plugin"]
-edition = "2018"
+edition = "2021"
 
 [dependencies]
 cargo_metadata = "0.12"
index b4c4ca016aace000f6e8d9ed438e83f5325b7b51..15252ef96cd1d7fbaf5662eaf4615eafb54873c8 100644 (file)
@@ -2,10 +2,9 @@
 use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks};
 use rustc_hir::{
     def::{DefKind, Res},
-    Body, Expr, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node, QPath,
+    Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::TypeFoldable;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
 
@@ -68,6 +67,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
             if let ItemKind::Impl(Impl {
                 of_trait: Some(ref trait_ref),
                 items: [child],
+                self_ty,
                 ..
             }) = item.kind;
             if let attrs = cx.tcx.hir().attrs(item.hir_id());
@@ -80,9 +80,18 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
             if let ImplItemKind::Fn(_, b) = &impl_item.kind;
             if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
             if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
+            if !attrs.iter().any(|attr| attr.doc_str().is_some());
+            if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
+            if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
             then {
-                if cx.tcx.type_of(item.def_id).definitely_has_param_types_or_consts(cx.tcx) {
-                    return;
+                if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
+                    if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
+                        for arg in a.args {
+                            if !matches!(arg, GenericArg::Lifetime(_)) {
+                                return;
+                            }
+                        }
+                    }
                 }
                 let should_emit = match remove_blocks(func_expr).kind {
                     ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
index 7069cb4198ca954a138e3e134cbeaaa5520359b2..1167b26c8f15392727471040c614b556998fdd40 100644 (file)
@@ -1,33 +1,44 @@
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::fn_def_id;
 
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::{def::Res, def_id::DefId, Crate, Expr};
+use rustc_hir::{def::Res, def_id::DefIdMap, Crate, Expr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Symbol;
+
+use crate::utils::conf;
 
 declare_clippy_lint! {
     /// ### What it does
     /// Denies the configured methods and functions in clippy.toml
     ///
     /// ### Why is this bad?
-    /// Some methods are undesirable in certain contexts,
-    /// and it's beneficial to lint for them as needed.
+    /// Some methods are undesirable in certain contexts, and it's beneficial to
+    /// lint for them as needed.
     ///
     /// ### Example
     /// An example clippy.toml configuration:
     /// ```toml
     /// # clippy.toml
-    /// disallowed-methods = ["std::vec::Vec::leak", "std::time::Instant::now"]
+    /// disallowed-methods = [
+    ///     # Can use a string as the path of the disallowed method.
+    ///     "std::boxed::Box::new",
+    ///     # Can also use an inline table with a `path` key.
+    ///     { path = "std::time::Instant::now" },
+    ///     # When using an inline table, can add a `reason` for why the method
+    ///     # is disallowed.
+    ///     { path = "std::vec::Vec::leak", reason = "no leaking memory" },
+    /// ]
     /// ```
     ///
     /// ```rust,ignore
     /// // Example code where clippy issues a warning
     /// let xs = vec![1, 2, 3, 4];
     /// xs.leak(); // Vec::leak is disallowed in the config.
+    /// // The diagnostic contains the message "no leaking memory".
     ///
     /// let _now = Instant::now(); // Instant::now is disallowed in the config.
+    ///
+    /// let _box = Box::new(3); // Box::new is disallowed in the config.
     /// ```
     ///
     /// Use instead:
 
 #[derive(Clone, Debug)]
 pub struct DisallowedMethod {
-    disallowed: FxHashSet<Vec<Symbol>>,
-    def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
+    conf_disallowed: Vec<conf::DisallowedMethod>,
+    disallowed: DefIdMap<Option<String>>,
 }
 
 impl DisallowedMethod {
-    pub fn new(disallowed: &FxHashSet<String>) -> Self {
+    pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self {
         Self {
-            disallowed: disallowed
-                .iter()
-                .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
-                .collect(),
-            def_ids: FxHashSet::default(),
+            conf_disallowed,
+            disallowed: DefIdMap::default(),
         }
     }
 }
@@ -63,32 +71,36 @@ pub fn new(disallowed: &FxHashSet<String>) -> Self {
 
 impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
     fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
-        for path in &self.disallowed {
-            let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
-            if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
-            {
-                self.def_ids.insert((id, path.clone()));
+        for conf in &self.conf_disallowed {
+            let (path, reason) = match conf {
+                conf::DisallowedMethod::Simple(path) => (path, None),
+                conf::DisallowedMethod::WithReason { path, reason } => (
+                    path,
+                    reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
+                ),
+            };
+            let segs: Vec<_> = path.split("::").collect();
+            if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
+                self.disallowed.insert(id, reason);
             }
         }
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let Some(def_id) = fn_def_id(cx, expr) {
-            if self.def_ids.iter().any(|(id, _)| def_id == *id) {
-                let func_path = cx.get_def_path(def_id);
-                let func_path_string = func_path
-                    .into_iter()
-                    .map(Symbol::to_ident_string)
-                    .collect::<Vec<_>>()
-                    .join("::");
-
-                span_lint(
-                    cx,
-                    DISALLOWED_METHOD,
-                    expr.span,
-                    &format!("use of a disallowed method `{}`", func_path_string),
-                );
+        let def_id = match fn_def_id(cx, expr) {
+            Some(def_id) => def_id,
+            None => return,
+        };
+        let reason = match self.disallowed.get(&def_id) {
+            Some(reason) => reason,
+            None => return,
+        };
+        let func_path = cx.tcx.def_path_str(def_id);
+        let msg = format!("use of a disallowed method `{}`", func_path);
+        span_lint_and_then(cx, DISALLOWED_METHOD, expr.span, &msg, |diag| {
+            if let Some(reason) = reason {
+                diag.note(reason);
             }
-        }
+        });
     }
 }
index e627168b9327566fb67015d9652c5c9f762a28c9..6c861fb33a9787fa407b7b2d7cd536697a30b0bc 100644 (file)
@@ -48,7 +48,7 @@ pub fn new(disallowed: &FxHashSet<String>) -> Self {
         Self {
             disallowed: disallowed
                 .iter()
-                .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
+                .map(|s| s.split("::").map(Symbol::intern).collect::<Vec<_>>())
                 .collect(),
             def_ids: FxHashSet::default(),
             prim_tys: FxHashSet::default(),
index 6bbac6d9a24688598775794ba0f94a991eb1c27c..090be73af3b7a9f073f6ced9cf7cb598d02f5c1b 100644 (file)
@@ -91,8 +91,11 @@ fn check_fn(
                     if trait_item.id.hir_id() == hir_id {
                         // be sure we have `self` parameter in this function
                         if let AssocItemKind::Fn { has_self: true } = trait_item.kind {
-                            trait_self_ty =
-                                Some(TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id()).self_ty().skip_binder());
+                            trait_self_ty = Some(
+                                TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id())
+                                    .self_ty()
+                                    .skip_binder(),
+                            );
                         }
                     }
                 }
index f6a64a8ca6031f7a352595ed48d0f283ba8ea8da..9df92cc5b640667faf06c1f63dcf3b3b634d7541 100644 (file)
@@ -1,16 +1,16 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
 use clippy_utils::usage::UsedAfterExprVisitor;
-use clippy_utils::{get_enclosing_loop_or_closure, higher};
-use clippy_utils::{is_adjusted, iter_input_pats};
+use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, ClosureKind, Ty};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Expr, ExprKind, Param, PatKind, Unsafety};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
+use rustc_middle::ty::subst::Subst;
+use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
     /// ### Why is this bad?
     /// It's unnecessary to create the closure.
     ///
-    /// ### Known problems
-    /// [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),
-    /// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),
-    /// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)
-    ///
-    ///
     /// ### Example
     /// ```rust,ignore
     /// Some('a').map(|s| s.to_uppercase());
 
 impl<'tcx> LateLintPass<'tcx> for EtaReduction {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_external_macro(cx.sess(), expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
-
-        match expr.kind {
-            ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => {
-                for arg in args {
-                    // skip `foo(macro!())`
-                    if arg.span.ctxt() == expr.span.ctxt() {
-                        check_closure(cx, arg);
-                    }
-                }
-            },
-            _ => (),
-        }
-    }
-}
-
-fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-    if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind {
-        let body = cx.tcx.hir().body(eid);
-        let ex = &body.value;
-
-        if ex.span.ctxt() != expr.span.ctxt() {
-            if decl.inputs.is_empty() {
-                if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, ex) {
+        let body = match expr.kind {
+            ExprKind::Closure(_, _, id, _, _) => cx.tcx.hir().body(id),
+            _ => return,
+        };
+        if body.value.span.from_expansion() {
+            if body.params.is_empty() {
+                if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) {
                     // replace `|| vec![]` with `Vec::new`
                     span_lint_and_sugg(
                         cx,
@@ -117,33 +95,30 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             return;
         }
 
-        if_chain!(
-            if let ExprKind::Call(caller, args) = ex.kind;
-
-            if let ExprKind::Path(_) = caller.kind;
-
-            // Not the same number of arguments, there is no way the closure is the same as the function return;
-            if args.len() == decl.inputs.len();
-
-            // Are the expression or the arguments type-adjusted? Then we need the closure
-            if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg)));
-
-            let fn_ty = cx.typeck_results().expr_ty(caller);
-
-            if matches!(fn_ty.kind(), ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _));
-
-            if !type_is_unsafe_function(cx, fn_ty);
-
-            if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
+        let closure_ty = cx.typeck_results().expr_ty(expr);
 
+        if_chain!(
+            if let ExprKind::Call(callee, args) = body.value.kind;
+            if let ExprKind::Path(_) = callee.kind;
+            if check_inputs(cx, body.params, args);
+            let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
+            let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
+                .map_or(callee_ty, |id| cx.tcx.type_of(id));
+            if check_sig(cx, closure_ty, call_ty);
+            let substs = cx.typeck_results().node_substs(callee.hir_id);
+            // This fixes some false positives that I don't entirely understand
+            if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions();
+            // A type param function ref like `T::f` is not 'static, however
+            // it is if cast like `T::f as fn()`. This seems like a rustc bug.
+            if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
             then {
                 span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
-                    if let Some(mut snippet) = snippet_opt(cx, caller.span) {
+                    if let Some(mut snippet) = snippet_opt(cx, callee.span) {
                         if_chain! {
-                            if let ty::Closure(_, substs) = fn_ty.kind();
+                            if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
                             if let ClosureKind::FnMut = substs.as_closure().kind();
-                            if UsedAfterExprVisitor::is_found(cx, caller)
-                                || get_enclosing_loop_or_closure(cx.tcx, expr).is_some();
+                            if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
+                                || UsedAfterExprVisitor::is_found(cx, callee);
 
                             then {
                                 // Mutable closure is used after current expr; we cannot consume it.
@@ -162,110 +137,79 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         );
 
         if_chain!(
-            if let ExprKind::MethodCall(path, _, args, _) = ex.kind;
-
-            // Not the same number of arguments, there is no way the closure is the same as the function return;
-            if args.len() == decl.inputs.len();
-
-            // Are the expression or the arguments type-adjusted? Then we need the closure
-            if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg)));
-
-            let method_def_id = cx.typeck_results().type_dependent_def_id(ex.hir_id).unwrap();
-            if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id));
-
-            if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
-
-            if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]);
-
+            if let ExprKind::MethodCall(path, _, args, _) = body.value.kind;
+            if check_inputs(cx, body.params, args);
+            let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
+            let substs = cx.typeck_results().node_substs(body.value.hir_id);
+            let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs);
+            if check_sig(cx, closure_ty, call_ty);
             then {
-                span_lint_and_sugg(
-                    cx,
-                    REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
-                    expr.span,
-                    "redundant closure",
-                    "replace the closure with the method itself",
-                    format!("{}::{}", name, path.ident.name),
-                    Applicability::MachineApplicable,
-                );
+                span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
+                    let name = get_ufcs_type_name(cx, method_def_id);
+                    diag.span_suggestion(
+                        expr.span,
+                        "replace the closure with the method itself",
+                        format!("{}::{}", name, path.ident.name),
+                        Applicability::MachineApplicable,
+                    );
+                })
             }
         );
     }
 }
 
-/// Tries to determine the type for universal function call to be used instead of the closure
-fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_arg: &Expr<'_>) -> Option<String> {
-    let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0];
-    let actual_type_of_self = &cx.typeck_results().node_type(self_arg.hir_id);
-
-    if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) {
-        if match_borrow_depth(expected_type_of_self, actual_type_of_self)
-            && implements_trait(cx, actual_type_of_self, trait_id, &[])
-        {
-            return Some(cx.tcx.def_path_str(trait_id));
-        }
+fn check_inputs(cx: &LateContext<'_>, params: &[Param<'_>], call_args: &[Expr<'_>]) -> bool {
+    if params.len() != call_args.len() {
+        return false;
     }
-
-    cx.tcx.impl_of_method(method_def_id).and_then(|_| {
-        //a type may implicitly implement other type's methods (e.g. Deref)
-        if match_types(expected_type_of_self, actual_type_of_self) {
-            Some(get_type_name(cx, actual_type_of_self))
-        } else {
-            None
+    std::iter::zip(params, call_args).all(|(param, arg)| {
+        match param.pat.kind {
+            PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
+            _ => return false,
+        }
+        match *cx.typeck_results().expr_adjustments(arg) {
+            [] => true,
+            [Adjustment {
+                kind: Adjust::Deref(None),
+                ..
+            }, Adjustment {
+                kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
+                ..
+            }] => {
+                // re-borrow with the same mutability is allowed
+                let ty = cx.typeck_results().expr_ty(arg);
+                matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into())
+            },
+            _ => false,
         }
     })
 }
 
-fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
-    match (&lhs.kind(), &rhs.kind()) {
-        (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(t1, t2),
-        (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))),
-    }
-}
-
-fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
-    match (&lhs.kind(), &rhs.kind()) {
-        (ty::Bool, ty::Bool)
-        | (ty::Char, ty::Char)
-        | (ty::Int(_), ty::Int(_))
-        | (ty::Uint(_), ty::Uint(_))
-        | (ty::Str, ty::Str) => true,
-        (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_types(t1, t2),
-        (ty::Array(t1, _), ty::Array(t2, _)) | (ty::Slice(t1), ty::Slice(t2)) => match_types(t1, t2),
-        (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2,
-        (_, _) => false,
+fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
+    let call_sig = call_ty.fn_sig(cx.tcx);
+    if call_sig.unsafety() == Unsafety::Unsafe {
+        return false;
     }
-}
-
-fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String {
-    match ty.kind() {
-        ty::Adt(t, _) => cx.tcx.def_path_str(t.did),
-        ty::Ref(_, r, _) => get_type_name(cx, r),
-        _ => ty.to_string(),
+    if !closure_ty.has_late_bound_regions() {
+        return true;
     }
+    let substs = match closure_ty.kind() {
+        ty::Closure(_, substs) => substs,
+        _ => return false,
+    };
+    let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal);
+    cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
 }
 
-fn compare_inputs(
-    closure_inputs: &mut dyn Iterator<Item = &Param<'_>>,
-    call_args: &mut dyn Iterator<Item = &Expr<'_>>,
-) -> bool {
-    for (closure_input, function_arg) in closure_inputs.zip(call_args) {
-        if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind {
-            // XXXManishearth Should I be checking the binding mode here?
-            if let ExprKind::Path(QPath::Resolved(None, p)) = function_arg.kind {
-                if p.segments.len() != 1 {
-                    // If it's a proper path, it can't be a local variable
-                    return false;
-                }
-                if p.segments[0].ident.name != ident.name {
-                    // The two idents should be the same
-                    return false;
-                }
-            } else {
-                return false;
+fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
+    match cx.tcx.associated_item(method_def_id).container {
+        ty::TraitContainer(def_id) => cx.tcx.def_path_str(def_id),
+        ty::ImplContainer(def_id) => {
+            let ty = cx.tcx.type_of(def_id);
+            match ty.kind() {
+                ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did),
+                _ => ty.to_string(),
             }
-        } else {
-            return false;
-        }
+        },
     }
-    true
 }
index a3d70f31f0021b90ae89f334d02eccd817dd68c5..1e8a5bd7d34453be68f0b322047f601ed1f789d6 100644 (file)
@@ -111,7 +111,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                             Applicability::MachineApplicable,
                         );
                     }
-                } else if digits > max as usize && sym_str != float_str {
+                } else if digits > max as usize && float_str.len() < sym_str.len() {
                     span_lint_and_sugg(
                         cx,
                         EXCESSIVE_PRECISION,
index 863c606f5a92cf158b4ee4935663916a8a4b0d75..508cac33848f76d0b081bbc5189c3882894c2fa3 100644 (file)
@@ -69,8 +69,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     ty::Str => true,
                     _ => false,
                 };
-                if format_args.args.iter().all(|e| is_display_arg(e));
-                if format_args.fmt_expr.map_or(true, |e| check_unformatted(e));
+                if format_args.args.iter().all(is_display_arg);
+                if format_args.fmt_expr.map_or(true, check_unformatted);
                 then {
                     let is_new_string = match value.kind {
                         ExprKind::Binary(..) => true,
index 4dd0ffe77ea49fbff542b3c20a48ad053aa94650..b4f186525c562ea4cd33c4dcdcf9bddd08ed1d92 100644 (file)
@@ -286,34 +286,39 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
 }
 
 fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
-    if !differing_macro_contexts(first.span, second.span)
-        && !first.span.from_expansion()
-        && is_if(first)
-        && (is_block(second) || is_if(second))
-    {
-        // where the else would be
-        let else_span = first.span.between(second.span);
+    if_chain! {
+        if !differing_macro_contexts(first.span, second.span);
+        if !first.span.from_expansion();
+        if let ExprKind::If(cond_expr, ..) = &first.kind;
+        if is_block(second) || is_if(second);
 
-        if let Some(else_snippet) = snippet_opt(cx, else_span) {
-            if !else_snippet.contains('\n') {
-                let (looks_like, next_thing) = if is_if(second) {
-                    ("an `else if`", "the second `if`")
-                } else {
-                    ("an `else {..}`", "the next block")
-                };
+        // Proc-macros can give weird spans. Make sure this is actually an `if`.
+        if let Some(if_snip) = snippet_opt(cx, first.span.until(cond_expr.span));
+        if if_snip.starts_with("if");
 
-                span_lint_and_note(
-                    cx,
-                    SUSPICIOUS_ELSE_FORMATTING,
-                    else_span,
-                    &format!("this looks like {} but the `else` is missing", looks_like),
-                    None,
-                    &format!(
-                        "to remove this lint, add the missing `else` or add a new line before {}",
-                        next_thing,
-                    ),
-                );
-            }
+        // If there is a line break between the two expressions, don't lint.
+        // If there is a non-whitespace character, this span came from a proc-macro.
+        let else_span = first.span.between(second.span);
+        if let Some(else_snippet) = snippet_opt(cx, else_span);
+        if !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace());
+        then {
+            let (looks_like, next_thing) = if is_if(second) {
+                ("an `else if`", "the second `if`")
+            } else {
+                ("an `else {..}`", "the next block")
+            };
+
+            span_lint_and_note(
+                cx,
+                SUSPICIOUS_ELSE_FORMATTING,
+                else_span,
+                &format!("this looks like {} but the `else` is missing", looks_like),
+                None,
+                &format!(
+                    "to remove this lint, add the missing `else` or add a new line before {}",
+                    next_thing,
+                ),
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs
deleted file mode 100644 (file)
index adcd78e..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
-use clippy_utils::method_chain_args;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, PatKind, QPath};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    ///* Checks for unnecessary `ok()` in if let.
-    ///
-    /// ### Why is this bad?
-    /// Calling `ok()` in if let is unnecessary, instead match
-    /// on `Ok(pat)`
-    ///
-    /// ### Example
-    /// ```ignore
-    /// for i in iter {
-    ///     if let Some(value) = i.parse().ok() {
-    ///         vec.push(value)
-    ///     }
-    /// }
-    /// ```
-    /// Could be written:
-    ///
-    /// ```ignore
-    /// for i in iter {
-    ///     if let Ok(value) = i.parse() {
-    ///         vec.push(value)
-    ///     }
-    /// }
-    /// ```
-    pub IF_LET_SOME_RESULT,
-    style,
-    "usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
-}
-
-declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
-
-impl<'tcx> LateLintPass<'tcx> for OkIfLet {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! { //begin checking variables
-            if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr);
-            if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
-            if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _)  = let_pat.kind; //get operation
-            if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
-            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::result_type);
-            if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
-
-            then {
-                let mut applicability = Applicability::MachineApplicable;
-                let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
-                let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
-                let sugg = format!(
-                    "if let Ok({}) = {}",
-                    some_expr_string,
-                    trimmed_ok.trim().trim_end_matches('.'),
-                );
-                span_lint_and_sugg(
-                    cx,
-                    IF_LET_SOME_RESULT,
-                    expr.span.with_hi(let_expr.span.hi()),
-                    "matching on `Some` with `ok()` is redundant",
-                    &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
-                    sugg,
-                    applicability,
-                );
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/if_then_panic.rs b/src/tools/clippy/clippy_lints/src/if_then_panic.rs
new file mode 100644 (file)
index 0000000..ee575c8
--- /dev/null
@@ -0,0 +1,97 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::PanicExpn;
+use clippy_utils::is_expn_of;
+use clippy_utils::source::snippet_with_applicability;
+use rustc_errors::Applicability;
+use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects `if`-then-`panic!` that can be replaced with `assert!`.
+    ///
+    /// ### Why is this bad?
+    /// `assert!` is simpler than `if`-then-`panic!`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let sad_people: Vec<&str> = vec![];
+    /// if !sad_people.is_empty() {
+    ///     panic!("there are sad people: {:?}", sad_people);
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let sad_people: Vec<&str> = vec![];
+    /// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
+    /// ```
+    pub IF_THEN_PANIC,
+    style,
+    "`panic!` and only a `panic!` in `if`-then statement"
+}
+
+declare_lint_pass!(IfThenPanic => [IF_THEN_PANIC]);
+
+impl LateLintPass<'_> for IfThenPanic {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if_chain! {
+            if let Expr {
+                kind: ExprKind:: If(cond, Expr {
+                    kind: ExprKind::Block(
+                        Block {
+                            stmts: [stmt],
+                            ..
+                        },
+                        _),
+                    ..
+                }, None),
+                ..
+            } = &expr;
+            if is_expn_of(stmt.span, "panic").is_some();
+            if !matches!(cond.kind, ExprKind::Let(_, _, _));
+            if let StmtKind::Semi(semi) = stmt.kind;
+            if !cx.tcx.sess.source_map().is_multiline(cond.span);
+
+            then {
+                let span = if let Some(panic_expn) = PanicExpn::parse(semi) {
+                    match *panic_expn.format_args.value_args {
+                        [] => panic_expn.format_args.format_string_span,
+                        [.., last] => panic_expn.format_args.format_string_span.to(last.span),
+                    }
+                } else {
+                    if_chain! {
+                        if let ExprKind::Block(block, _) = semi.kind;
+                        if let Some(init) = block.expr;
+                        if let ExprKind::Call(_, [format_args]) = init.kind;
+
+                        then {
+                            format_args.span
+                        } else {
+                            return
+                        }
+                    }
+                };
+                let mut applicability = Applicability::MachineApplicable;
+                let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
+
+                let cond_sugg =
+                if let ExprKind::DropTemps(Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..}) = cond.kind {
+                    snippet_with_applicability(cx, not_expr.span, "..", &mut applicability).to_string()
+                } else {
+                    format!("!{}", snippet_with_applicability(cx, cond.span, "..", &mut applicability))
+                };
+
+                span_lint_and_sugg(
+                    cx,
+                    IF_THEN_PANIC,
+                    expr.span,
+                    "only a `panic!` in `if`-then statement",
+                    "try",
+                    format!("assert!({}, {});", cond_sugg, sugg),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
new file mode 100644 (file)
index 0000000..6c77934
--- /dev/null
@@ -0,0 +1,64 @@
+use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait};
+use rustc_hir::{ImplItem, ImplItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::kw;
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects methods named `iter` or `iter_mut` that do not have a return type that implements `Iterator`.
+    ///
+    /// ### Why is this bad?
+    /// Methods named `iter` or `iter_mut` conventionally return an `Iterator`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// // `String` does not implement `Iterator`
+    /// struct Data {}
+    /// impl Data {
+    ///     fn iter(&self) -> String {
+    ///         todo!()
+    ///     }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::str::Chars;
+    /// struct Data {}
+    /// impl Data {
+    ///    fn iter(&self) -> Chars<'static> {
+    ///        todo!()
+    ///    }
+    /// }
+    /// ```
+    pub ITER_NOT_RETURNING_ITERATOR,
+    pedantic,
+    "methods named `iter` or `iter_mut` that do not return an `Iterator`"
+}
+
+declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]);
+
+impl LateLintPass<'_> for IterNotReturningIterator {
+    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) {
+        let name: &str = &impl_item.ident.name.as_str();
+        if_chain! {
+            if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind;
+            let ret_ty = return_ty(cx, impl_item.hir_id());
+            if matches!(name, "iter" | "iter_mut");
+            if let [param] = cx.tcx.fn_arg_names(impl_item.def_id);
+            if param.name == kw::SelfLower;
+            if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+            if !implements_trait(cx, ret_ty, iter_trait_id, &[]);
+
+            then {
+                span_lint(
+                    cx,
+                    ITER_NOT_RETURNING_ITERATOR,
+                    fn_sig.span,
+                    &format!("this method is named `{}` but its return type does not implement `Iterator`", name),
+                );
+            }
+        }
+    }
+}
index acc78840bb9e6f2294547af70e89189896c6fe83..897249174822cad0893287dcce17e81e74f1ddb0 100644 (file)
@@ -225,8 +225,8 @@ macro_rules! declare_clippy_lint {
 mod get_last_with_len;
 mod identity_op;
 mod if_let_mutex;
-mod if_let_some_result;
 mod if_not_else;
+mod if_then_panic;
 mod if_then_some_else_none;
 mod implicit_hasher;
 mod implicit_return;
@@ -241,6 +241,7 @@ macro_rules! declare_clippy_lint {
 mod integer_division;
 mod invalid_upcast_comparisons;
 mod items_after_statements;
+mod iter_not_returning_iterator;
 mod large_const_arrays;
 mod large_enum_variant;
 mod large_stack_arrays;
@@ -262,6 +263,7 @@ macro_rules! declare_clippy_lint {
 mod map_err_ignore;
 mod map_unit_fn;
 mod match_on_vec_items;
+mod match_result_ok;
 mod matches;
 mod mem_discriminant;
 mod mem_forget;
@@ -330,6 +332,7 @@ macro_rules! declare_clippy_lint {
 mod regex;
 mod repeat_once;
 mod returns;
+mod same_name_method;
 mod self_assignment;
 mod self_named_constructors;
 mod semicolon_if_nothing_returned;
@@ -655,8 +658,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         get_last_with_len::GET_LAST_WITH_LEN,
         identity_op::IDENTITY_OP,
         if_let_mutex::IF_LET_MUTEX,
-        if_let_some_result::IF_LET_SOME_RESULT,
         if_not_else::IF_NOT_ELSE,
+        if_then_panic::IF_THEN_PANIC,
         if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
         implicit_hasher::IMPLICIT_HASHER,
         implicit_return::IMPLICIT_RETURN,
@@ -674,6 +677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         integer_division::INTEGER_DIVISION,
         invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
         items_after_statements::ITEMS_AFTER_STATEMENTS,
+        iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
         large_const_arrays::LARGE_CONST_ARRAYS,
         large_enum_variant::LARGE_ENUM_VARIANT,
         large_stack_arrays::LARGE_STACK_ARRAYS,
@@ -723,6 +727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         map_unit_fn::OPTION_MAP_UNIT_FN,
         map_unit_fn::RESULT_MAP_UNIT_FN,
         match_on_vec_items::MATCH_ON_VEC_ITEMS,
+        match_result_ok::MATCH_RESULT_OK,
         matches::INFALLIBLE_DESTRUCTURING_MATCH,
         matches::MATCH_AS_REF,
         matches::MATCH_BOOL,
@@ -908,6 +913,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         repeat_once::REPEAT_ONCE,
         returns::LET_AND_RETURN,
         returns::NEEDLESS_RETURN,
+        same_name_method::SAME_NAME_METHOD,
         self_assignment::SELF_ASSIGNMENT,
         self_named_constructors::SELF_NAMED_CONSTRUCTORS,
         semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
@@ -952,7 +958,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         transmuting_null::TRANSMUTING_NULL,
         try_err::TRY_ERR,
         types::BORROWED_BOX,
-        types::BOX_VEC,
+        types::BOX_COLLECTION,
         types::LINKEDLIST,
         types::OPTION_OPTION,
         types::RC_BUFFER,
@@ -1051,6 +1057,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(panic_unimplemented::UNIMPLEMENTED),
         LintId::of(panic_unimplemented::UNREACHABLE),
         LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
+        LintId::of(same_name_method::SAME_NAME_METHOD),
         LintId::of(shadow::SHADOW_REUSE),
         LintId::of(shadow::SHADOW_SAME),
         LintId::of(strings::STRING_ADD),
@@ -1104,6 +1111,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
         LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
         LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
+        LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
         LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
         LintId::of(let_underscore::LET_UNDERSCORE_DROP),
         LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
@@ -1126,6 +1134,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(methods::INEFFICIENT_TO_STRING),
         LintId::of(methods::MAP_FLATTEN),
         LintId::of(methods::MAP_UNWRAP_OR),
+        LintId::of(misc::FLOAT_CMP),
         LintId::of(misc::USED_UNDERSCORE_BINDING),
         LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
         LintId::of(mut_mut::MUT_MUT),
@@ -1134,6 +1143,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(needless_continue::NEEDLESS_CONTINUE),
         LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
         LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
+        LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
         LintId::of(non_expressive_names::SIMILAR_NAMES),
         LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
         LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
@@ -1249,7 +1259,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
         LintId::of(identity_op::IDENTITY_OP),
         LintId::of(if_let_mutex::IF_LET_MUTEX),
-        LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
+        LintId::of(if_then_panic::IF_THEN_PANIC),
         LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
         LintId::of(infinite_iter::INFINITE_ITER),
         LintId::of(inherent_to_string::INHERENT_TO_STRING),
@@ -1292,6 +1302,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(map_clone::MAP_CLONE),
         LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
         LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
+        LintId::of(match_result_ok::MATCH_RESULT_OK),
         LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
         LintId::of(matches::MATCH_AS_REF),
         LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
@@ -1358,7 +1369,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(minmax::MIN_MAX),
         LintId::of(misc::CMP_NAN),
         LintId::of(misc::CMP_OWNED),
-        LintId::of(misc::FLOAT_CMP),
         LintId::of(misc::MODULO_ONE),
         LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
         LintId::of(misc::TOPLEVEL_REF_ARG),
@@ -1390,7 +1400,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
         LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
         LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
-        LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
         LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
         LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
         LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
@@ -1448,7 +1457,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(transmuting_null::TRANSMUTING_NULL),
         LintId::of(try_err::TRY_ERR),
         LintId::of(types::BORROWED_BOX),
-        LintId::of(types::BOX_VEC),
+        LintId::of(types::BOX_COLLECTION),
         LintId::of(types::REDUNDANT_ALLOCATION),
         LintId::of(types::TYPE_COMPLEXITY),
         LintId::of(types::VEC_BOX),
@@ -1504,7 +1513,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(functions::DOUBLE_MUST_USE),
         LintId::of(functions::MUST_USE_UNIT),
         LintId::of(functions::RESULT_UNIT_ERR),
-        LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
+        LintId::of(if_then_panic::IF_THEN_PANIC),
         LintId::of(inherent_to_string::INHERENT_TO_STRING),
         LintId::of(len_zero::COMPARISON_TO_EMPTY),
         LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
@@ -1520,6 +1529,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(manual_map::MANUAL_MAP),
         LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
         LintId::of(map_clone::MAP_CLONE),
+        LintId::of(match_result_ok::MATCH_RESULT_OK),
         LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
         LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
         LintId::of(matches::MATCH_OVERLAPPING_ARM),
@@ -1564,7 +1574,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
         LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
         LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
-        LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
         LintId::of(ptr::CMP_NULL),
         LintId::of(ptr::PTR_ARG),
         LintId::of(ptr_eq::PTR_EQ),
@@ -1724,7 +1733,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(methods::ZST_OFFSET),
         LintId::of(minmax::MIN_MAX),
         LintId::of(misc::CMP_NAN),
-        LintId::of(misc::FLOAT_CMP),
         LintId::of(misc::MODULO_ONE),
         LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
         LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
@@ -1787,7 +1795,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(redundant_clone::REDUNDANT_CLONE),
         LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
         LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
-        LintId::of(types::BOX_VEC),
+        LintId::of(types::BOX_COLLECTION),
         LintId::of(types::REDUNDANT_ALLOCATION),
         LintId::of(vec::USELESS_VEC),
         LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
@@ -1920,6 +1928,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
 
     store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
+    store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
     store.register_late_pass(|| Box::new(map_clone::MapClone));
     store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
     store.register_late_pass(|| Box::new(shadow::Shadow));
@@ -1952,7 +1961,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
     store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
     store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
-    store.register_late_pass(|| Box::new(regex::Regex::default()));
+    store.register_late_pass(|| Box::new(regex::Regex));
     store.register_late_pass(|| Box::new(copies::CopyAndPaste));
     store.register_late_pass(|| Box::new(copy_iterator::CopyIterator));
     store.register_late_pass(|| Box::new(format::UselessFormat));
@@ -1976,7 +1985,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
     store.register_late_pass(|| Box::new(missing_inline::MissingInline));
     store.register_late_pass(move || Box::new(exhaustive_items::ExhaustiveItems));
-    store.register_late_pass(|| Box::new(if_let_some_result::OkIfLet));
+    store.register_late_pass(|| Box::new(match_result_ok::MatchResultOk));
     store.register_late_pass(|| Box::new(partialeq_ne_impl::PartialEqNeImpl));
     store.register_late_pass(|| Box::new(unused_io_amount::UnusedIoAmount));
     let enum_variant_size_threshold = conf.enum_variant_size_threshold;
@@ -2105,8 +2114,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
     store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
     store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
-    let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
-    store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(&disallowed_methods)));
+    let disallowed_methods = conf.disallowed_methods.clone();
+    store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(disallowed_methods.clone())));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
     store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
@@ -2131,6 +2140,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
     store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors));
     store.register_late_pass(move || Box::new(feature_name::FeatureName));
+    store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
+    store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic));
 }
 
 #[rustfmt::skip]
@@ -2186,6 +2197,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
     ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
     ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
     ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
+    ls.register_renamed("clippy::box_vec", "clippy::box_collection");
     ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
     ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
     ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
@@ -2200,6 +2212,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
     ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
     ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
     ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
+    ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
 
     // uplifted lints
     ls.register_renamed("clippy::invalid_ref", "invalid_value");
index 12ffe7a136457d67219c83c78a2f443cae616b6b..dd60e460d21fe3b9cfbac874120caa134f9f1c03 100644 (file)
 use rustc_span::sym;
 
 /// Checks for the `FOR_KV_MAP` lint.
-pub(super) fn check<'tcx>(
-    cx: &LateContext<'tcx>,
-    pat: &'tcx Pat<'_>,
-    arg: &'tcx Expr<'_>,
-    body: &'tcx Expr<'_>,
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
     let pat_span = pat.span;
 
     if let PatKind::Tuple(pat, _) = pat.kind {
index 1848f5b5de2f2fca48c3f2c1d1d2ce00ce6a9d3a..4dcd5c87722eec0f8c9e43f0e4f9fa6a4efc0cc5 100644 (file)
@@ -65,7 +65,7 @@ fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
     match expr.kind {
         ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
-        ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
+        ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, is_simple_break_expr),
         _ => false,
     }
 }
index 79527e3bfa92f1b4b790d88319de759db0d8de5d..b390476a664d9e54f4c4490df4763204cc9c3265 100644 (file)
@@ -8,7 +8,7 @@
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
-use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
+use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp};
 use rustc_lint::LateContext;
 use rustc_span::{symbol::sym, Span, Symbol};
 
@@ -47,13 +47,8 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
     // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
     // afterwards a mutable borrow of a field isn't necessary.
-    let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
-        if cx.typeck_results().node_type(iter_expr.hir_id).ref_mutability() == Some(Mutability::Mut) {
-            // Reborrow for mutable references. It may not be possible to get a mutable reference here.
-            "&mut *"
-        } else {
-            "&mut "
-        }
+    let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
+        ".by_ref()"
     } else {
         ""
     };
@@ -65,7 +60,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         expr.span.with_hi(scrutinee_expr.span.hi()),
         "this loop could be written as a `for` loop",
         "try",
-        format!("for {} in {}{}", loop_var, ref_mut, iterator),
+        format!("for {} in {}{}", loop_var, iterator, by_ref),
         applicability,
     );
 }
@@ -74,8 +69,6 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 struct IterExpr {
     /// The span of the whole expression, not just the path and fields stored here.
     span: Span,
-    /// The HIR id of the whole expression, not just the path and fields stored here.
-    hir_id: HirId,
     /// The fields used, in order of child to parent.
     fields: Vec<Symbol>,
     /// The path being used.
@@ -86,14 +79,12 @@ struct IterExpr {
 /// the expression might have side effects.
 fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
     let span = e.span;
-    let hir_id = e.hir_id;
     let mut fields = Vec::new();
     loop {
         match e.kind {
             ExprKind::Path(ref path) => {
                 break Some(IterExpr {
                     span,
-                    hir_id,
                     fields,
                     path: cx.qpath_res(path, e.hir_id),
                 });
index 41e6ad12d058923a9af3d1a1b616bd7d650ced23..aff6b3853a4686f02ad3ffac031b76c382c9c926 100644 (file)
@@ -63,29 +63,24 @@ impl MacroUseImports {
     fn push_unique_macro(&mut self, cx: &LateContext<'_>, span: Span) {
         let call_site = span.source_callsite();
         let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
-        if let Some(_callee) = span.source_callee() {
-            if !self.collected.contains(&call_site) {
-                let name = if name.contains("::") {
-                    name.split("::").last().unwrap().to_string()
-                } else {
-                    name.to_string()
-                };
+        if span.source_callee().is_some() && !self.collected.contains(&call_site) {
+            let name = if name.contains("::") {
+                name.split("::").last().unwrap().to_string()
+            } else {
+                name.to_string()
+            };
 
-                self.mac_refs.push(MacroRefData::new(name));
-                self.collected.insert(call_site);
-            }
+            self.mac_refs.push(MacroRefData::new(name));
+            self.collected.insert(call_site);
         }
     }
 
     fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_>, span: Span) {
         let call_site = span.source_callsite();
         let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
-        if let Some(_callee) = span.source_callee() {
-            if !self.collected.contains(&call_site) {
-                self.mac_refs
-                    .push(MacroRefData::new(name.to_string()));
-                self.collected.insert(call_site);
-            }
+        if span.source_callee().is_some() && !self.collected.contains(&call_site) {
+            self.mac_refs.push(MacroRefData::new(name.to_string()));
+            self.collected.insert(call_site);
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
new file mode 100644 (file)
index 0000000..c7de06f
--- /dev/null
@@ -0,0 +1,89 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
+use clippy_utils::method_chain_args;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, PatKind, QPath};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for unnecessary `ok()` in `while let`.
+    ///
+    /// ### Why is this bad?
+    /// Calling `ok()` in `while let` is unnecessary, instead match
+    /// on `Ok(pat)`
+    ///
+    /// ### Example
+    /// ```ignore
+    /// while let Some(value) = iter.next().ok() {
+    ///     vec.push(value)
+    /// }
+    ///
+    /// if let Some(valie) = iter.next().ok() {
+    ///     vec.push(value)
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```ignore
+    /// while let Ok(value) = iter.next() {
+    ///     vec.push(value)
+    /// }
+    ///
+    /// if let Ok(value) = iter.next() {
+    ///        vec.push_value)
+    /// }
+    /// ```
+    pub MATCH_RESULT_OK,
+    style,
+    "usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
+}
+
+declare_lint_pass!(MatchResultOk => [MATCH_RESULT_OK]);
+
+impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let (let_pat, let_expr, ifwhile) =
+            if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
+                (let_pat, let_expr, "if")
+            } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
+                (let_pat, let_expr, "while")
+            } else {
+                return;
+            };
+
+        if_chain! {
+            if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
+            if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _)  = let_pat.kind; //get operation
+            if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
+            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::result_type);
+            if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
+
+            then {
+
+                let mut applicability = Applicability::MachineApplicable;
+                let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
+                let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
+                let sugg = format!(
+                    "{} let Ok({}) = {}",
+                    ifwhile,
+                    some_expr_string,
+                    trimmed_ok.trim().trim_end_matches('.'),
+                );
+                span_lint_and_sugg(
+                    cx,
+                    MATCH_RESULT_OK,
+                    expr.span.with_hi(let_expr.span.hi()),
+                    "matching on `Some` with `ok()` is redundant",
+                    &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
+                    sugg,
+                    applicability,
+                );
+            }
+        }
+    }
+}
index 2f1ff567e844f803c7e4f8930aefd1a5f1187eda..d878fbc35fdc27cf6360cfc8f8cafee72522e469 100644 (file)
     /// ```rust
     /// let x = 5;
     /// match x {
-    ///     1...10 => println!("1 ... 10"),
-    ///     5...15 => println!("5 ... 15"),
+    ///     1..=10 => println!("1 ... 10"),
+    ///     5..=15 => println!("5 ... 15"),
     ///     _ => (),
     /// }
     /// ```
index 001676503242b50b3b7e01b69ebb75896646afac..55688677e1d141d423d39ef222c1a0a964d006b8 100644 (file)
@@ -17,32 +17,25 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
     }
 
     let ctxt = expr.span.ctxt();
-    let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) {
+    let (method_name, msg, reverse) = if method_name == "splitn" {
+        ("split_once", "manual implementation of `split_once`", false)
+    } else {
+        ("rsplit_once", "manual implementation of `rsplit_once`", true)
+    };
+    let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
         Some(x) => x,
         None => return,
     };
-    let (method_name, msg) = if method_name == "splitn" {
-        ("split_once", "manual implementation of `split_once`")
-    } else {
-        ("rsplit_once", "manual implementation of `rsplit_once`")
-    };
 
     let mut app = Applicability::MachineApplicable;
     let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
     let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
 
-    match usage.kind {
+    let sugg = match usage.kind {
         IterUsageKind::NextTuple => {
-            span_lint_and_sugg(
-                cx,
-                MANUAL_SPLIT_ONCE,
-                usage.span,
-                msg,
-                "try this",
-                format!("{}.{}({})", self_snip, method_name, pat_snip),
-                app,
-            );
+            format!("{}.{}({})", self_snip, method_name, pat_snip)
         },
+        IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
         IterUsageKind::Next => {
             let self_deref = {
                 let adjust = cx.typeck_results().expr_adjustments(self_arg);
@@ -58,7 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
                     "*".repeat(adjust.len() - 2)
                 }
             };
-            let sugg = if usage.unwrap_kind.is_some() {
+            if usage.unwrap_kind.is_some() {
                 format!(
                     "{}.{}({}).map_or({}{}, |x| x.0)",
                     &self_snip, method_name, pat_snip, self_deref, &self_snip
@@ -68,9 +61,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
                     "Some({}.{}({}).map_or({}{}, |x| x.0))",
                     &self_snip, method_name, pat_snip, self_deref, &self_snip
                 )
-            };
-
-            span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
+            }
         },
         IterUsageKind::Second => {
             let access_str = match usage.unwrap_kind {
@@ -78,23 +69,18 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
                 Some(UnwrapKind::QuestionMark) => "?.1",
                 None => ".map(|x| x.1)",
             };
-            span_lint_and_sugg(
-                cx,
-                MANUAL_SPLIT_ONCE,
-                usage.span,
-                msg,
-                "try this",
-                format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str),
-                app,
-            );
+            format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str)
         },
-    }
+    };
+
+    span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
 }
 
 enum IterUsageKind {
     Next,
     Second,
     NextTuple,
+    RNextTuple,
 }
 
 enum UnwrapKind {
@@ -108,10 +94,12 @@ struct IterUsage {
     span: Span,
 }
 
+#[allow(clippy::too_many_lines)]
 fn parse_iter_usage(
     cx: &LateContext<'tcx>,
     ctxt: SyntaxContext,
     mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
+    reverse: bool,
 ) -> Option<IterUsage> {
     let (kind, span) = match iter.next() {
         Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
@@ -124,20 +112,30 @@ fn parse_iter_usage(
             let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
 
             match (&*name.ident.as_str(), args) {
-                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Next, e.span),
+                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+                    if reverse {
+                        (IterUsageKind::Second, e.span)
+                    } else {
+                        (IterUsageKind::Next, e.span)
+                    }
+                },
                 ("next_tuple", []) => {
-                    if_chain! {
+                    return if_chain! {
                         if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
                         if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
                         if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did);
                         if let ty::Tuple(subs) = subs.type_at(0).kind();
                         if subs.len() == 2;
                         then {
-                            return Some(IterUsage { kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None });
+                            Some(IterUsage {
+                                kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
+                                span: e.span,
+                                unwrap_kind: None
+                            })
                         } else {
-                            return None;
+                            None
                         }
-                    }
+                    };
                 },
                 ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
                     if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
@@ -158,7 +156,7 @@ fn parse_iter_usage(
                                 }
                             }
                         };
-                        match idx {
+                        match if reverse { idx ^ 1 } else { idx } {
                             0 => (IterUsageKind::Next, span),
                             1 => (IterUsageKind::Second, span),
                             _ => return None,
index 8a699f13f2ed267328eeca69fed865048b05225c..2025056ac94c3365a398dfb22987be6513a47791 100644 (file)
     /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
     /// (see e.g. the `std::string::ToString` trait).
     ///
+    /// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
+    ///
     /// Please find more info here:
     /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
     ///
index 538fa4e1678fc348b90da7ca3aaf22fb6ca5fc5a..0f32cd9164e2d7ecf874923a3a5d7a299b19365a 100644 (file)
     /// if (y - x).abs() > error_margin { }
     /// ```
     pub FLOAT_CMP,
-    correctness,
+    pedantic,
     "using `==` or `!=` on float values instead of comparing difference with an epsilon"
 }
 
index 2c7681c45a46292edd37b5ca2b88c6473dc0ff23..cb17e4dbfd0da8cdcf3930b9b32b4e88e5174a78 100644 (file)
@@ -3,7 +3,7 @@
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::TypeFoldable;
-use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut};
+use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::sym;
     /// so having types with interior mutability is a bad idea.
     ///
     /// ### Known problems
-    /// It's correct to use a struct, that contains interior mutability
-    /// as a key, when its `Hash` implementation doesn't access any of the interior mutable types.
-    /// However, this lint is unable to recognize this, so it causes a false positive in theses cases.
-    /// The `bytes` crate is a great example of this.
+    ///
+    /// #### False Positives
+    /// It's correct to use a struct that contains interior mutability as a key, when its
+    /// implementation of `Hash` or `Ord` doesn't access any of the interior mutable types.
+    /// However, this lint is unable to recognize this, so it will often cause false positives in
+    /// theses cases.  The `bytes` crate is a great example of this.
+    ///
+    /// #### False Negatives
+    /// For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind
+    /// indirection.  For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable
+    /// and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`.
+    ///
+    /// This lint does check a few cases for indirection.  Firstly, using some standard library
+    /// types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and
+    /// `BTreeSet`) directly as keys (e.g. in `HashMap<Box<Cell<usize>>, ()>`) **will** trigger the
+    /// lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their
+    /// contained type.
+    ///
+    /// Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`)
+    /// apply only to the **address** of the contained value.  Therefore, interior mutability
+    /// behind raw pointers (e.g. in `HashSet<*mut Cell<usize>>`) can't impact the value of `Hash`
+    /// or `Ord`, and therefore will not trigger this link.  For more info, see issue
+    /// [#6745](https://github.com/rust-lang/rust-clippy/issues/6745).
     ///
     /// ### Example
     /// ```rust
@@ -103,30 +122,52 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::
 fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
     let ty = ty.peel_refs();
     if let Adt(def, substs) = ty.kind() {
-        if [sym::hashmap_type, sym::BTreeMap, sym::hashset_type, sym::BTreeMap]
+        let is_keyed_type = [sym::hashmap_type, sym::BTreeMap, sym::hashset_type, sym::BTreeSet]
             .iter()
-            .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did))
-            && is_mutable_type(cx, substs.type_at(0), span)
-        {
+            .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did));
+        if is_keyed_type && is_interior_mutable_type(cx, substs.type_at(0), span) {
             span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
         }
     }
 }
 
-fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
+/// Determines if a type contains interior mutability which would affect its implementation of
+/// [`Hash`] or [`Ord`].
+fn is_interior_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
     match *ty.kind() {
-        RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => {
-            mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span)
-        },
-        Slice(inner_ty) => is_mutable_type(cx, inner_ty, span),
+        Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || is_interior_mutable_type(cx, inner_ty, span),
+        Slice(inner_ty) => is_interior_mutable_type(cx, inner_ty, span),
         Array(inner_ty, size) => {
-            size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span)
+            size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0)
+                && is_interior_mutable_type(cx, inner_ty, span)
         },
-        Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
-        Adt(..) => {
-            !ty.has_escaping_bound_vars()
-                && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
-                && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
+        Tuple(..) => ty.tuple_fields().any(|ty| is_interior_mutable_type(cx, ty, span)),
+        Adt(def, substs) => {
+            // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
+            // that of their type parameters.  Note: we don't include `HashSet` and `HashMap`
+            // because they have no impl for `Hash` or `Ord`.
+            let is_std_collection = [
+                sym::option_type,
+                sym::result_type,
+                sym::LinkedList,
+                sym::vec_type,
+                sym::vecdeque_type,
+                sym::BTreeMap,
+                sym::BTreeSet,
+                sym::Rc,
+                sym::Arc,
+            ]
+            .iter()
+            .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did));
+            let is_box = Some(def.did) == cx.tcx.lang_items().owned_box();
+            if is_std_collection || is_box {
+                // The type is mutable if any of its type parameters are
+                substs.types().any(|ty| is_interior_mutable_type(cx, ty, span))
+            } else {
+                !ty.has_escaping_bound_vars()
+                    && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
+                    && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
+            }
         },
         _ => false,
     }
index ba8f9446af85e4a0850fdedffe1996e7d3b1aaf0..1b2495d764d2a0c650f9f866a153252fa5288b5a 100644 (file)
@@ -104,7 +104,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if e.span.from_expansion() {
             return;
         }
-        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = e.kind {
+        if let ExprKind::AddrOf(BorrowKind::Ref, mutability, inner) = e.kind {
             if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
                 for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
                     if let [Adjustment {
@@ -116,14 +116,20 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                         ..
                     }] = *adj3
                     {
+                        let help_msg_ty = if matches!(mutability, Mutability::Not) {
+                            format!("&{}", ty)
+                        } else {
+                            format!("&mut {}", ty)
+                        };
+
                         span_lint_and_then(
                             cx,
                             NEEDLESS_BORROW,
                             e.span,
                             &format!(
-                                "this expression borrows a reference (`&{}`) that is immediately dereferenced \
+                                "this expression borrows a reference (`{}`) that is immediately dereferenced \
                              by the compiler",
-                                ty
+                                help_msg_ty
                             ),
                             |diag| {
                                 if let Some(snippet) = snippet_opt(cx, inner.span) {
index 2ffc00b449d08b7e9c25282579618b98ee233912..5b254bc8133d2fecce4c6227b90d7c91e2206b6c 100644 (file)
@@ -43,7 +43,7 @@
     /// let (a, b, c, d, e, f, g) = (...);
     /// ```
     pub MANY_SINGLE_CHAR_NAMES,
-    style,
+    pedantic,
     "too many single character bindings"
 }
 
index c0d1f1eb6e65ece72a714c7c2587636c5ac7e2a4..d696e17d656d49fd2427762e37657ac864ea373e 100644 (file)
@@ -372,7 +372,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
             for (_, ref mutbl, ref argspan) in decl
                 .inputs
                 .iter()
-                .filter_map(|ty| get_rptr_lm(ty))
+                .filter_map(get_rptr_lm)
                 .filter(|&(lt, _, _)| lt.name == out.name)
             {
                 if *mutbl == Mutability::Mut {
index 947c25ac880565cb946c4872e40e95641e2e293c..5d08aee1e5f8880f63e1382bb74635e3d85c32d1 100644 (file)
@@ -5,7 +5,7 @@
 use rustc_ast::ast::{LitKind, StrStyle};
 use rustc_hir::{BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::{BytePos, Span};
 use std::convert::TryFrom;
 
     "trivial regular expressions"
 }
 
-#[derive(Clone, Default)]
-pub struct Regex {}
-
-impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
+declare_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
 
 impl<'tcx> LateLintPass<'tcx> for Regex {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
index 341b5a61631dff896fb93510799316fb6cb324a3..ae85b7087e7b5add94b7cea8939f20f304f82377 100644 (file)
@@ -11,6 +11,7 @@
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::Span;
 use rustc_span::sym;
 
@@ -199,7 +200,9 @@ fn check_final_expr<'tcx>(
                 check_block_return(cx, ifblock);
             }
             if let Some(else_clause) = else_clause_opt {
-                check_final_expr(cx, else_clause, None, RetReplacement::Empty);
+                if expr.span.desugaring_kind() != Some(DesugaringKind::LetElse) {
+                    check_final_expr(cx, else_clause, None, RetReplacement::Empty);
+                }
             }
         },
         // a match expr, check all arms
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
new file mode 100644 (file)
index 0000000..014898e
--- /dev/null
@@ -0,0 +1,160 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Crate, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::AssocKind;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::Symbol;
+use rustc_span::Span;
+use std::collections::{BTreeMap, BTreeSet};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// It lints if a struct has two method with same time:
+    /// one from a trait, another not from trait.
+    ///
+    /// ### Why is this bad?
+    /// Confusing.
+    ///
+    /// ### Example
+    /// ```rust
+    /// trait T {
+    ///     fn foo(&self) {}
+    /// }
+    ///
+    /// struct S;
+    ///
+    /// impl T for S {
+    ///     fn foo(&self) {}
+    /// }
+    ///
+    /// impl S {
+    ///     fn foo(&self) {}
+    /// }
+    /// ```
+    pub SAME_NAME_METHOD,
+    restriction,
+    "two method with same name"
+}
+
+declare_lint_pass!(SameNameMethod => [SAME_NAME_METHOD]);
+
+struct ExistingName {
+    impl_methods: BTreeMap<Symbol, Span>,
+    trait_methods: BTreeMap<Symbol, Vec<Span>>,
+}
+
+impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
+    fn check_crate_post(&mut self, cx: &LateContext<'tcx>, krate: &'tcx Crate<'tcx>) {
+        let mut map = FxHashMap::<Res, ExistingName>::default();
+
+        for item in krate.items() {
+            if let ItemKind::Impl(Impl {
+                items,
+                of_trait,
+                self_ty,
+                ..
+            }) = &item.kind
+            {
+                if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind {
+                    if !map.contains_key(res) {
+                        map.insert(
+                            *res,
+                            ExistingName {
+                                impl_methods: BTreeMap::new(),
+                                trait_methods: BTreeMap::new(),
+                            },
+                        );
+                    }
+                    let existing_name = map.get_mut(res).unwrap();
+
+                    match of_trait {
+                        Some(trait_ref) => {
+                            let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
+                                if let Some(Node::TraitRef(TraitRef { path, .. })) =
+                                    cx.tcx.hir().find(trait_ref.hir_ref_id);
+                                if let Res::Def(DefKind::Trait, did) = path.res;
+                                then{
+                                    // FIXME: if
+                                    // `rustc_middle::ty::assoc::AssocItems::items` is public,
+                                    // we can iterate its keys instead of `in_definition_order`,
+                                    // which's more efficient
+                                    cx.tcx
+                                        .associated_items(did)
+                                        .in_definition_order()
+                                        .filter(|assoc_item| {
+                                            matches!(assoc_item.kind, AssocKind::Fn)
+                                        })
+                                        .map(|assoc_item| assoc_item.ident.name)
+                                        .collect()
+                                }else{
+                                    BTreeSet::new()
+                                }
+                            };
+
+                            let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
+                                if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
+                                    span_lint_and_then(
+                                        cx,
+                                        SAME_NAME_METHOD,
+                                        *impl_span,
+                                        "method's name is same to an existing method in a trait",
+                                        |diag| {
+                                            diag.span_note(
+                                                trait_method_span,
+                                                &format!("existing `{}` defined here", method_name),
+                                            );
+                                        },
+                                    );
+                                }
+                                if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
+                                    v.push(trait_method_span);
+                                } else {
+                                    existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
+                                }
+                            };
+
+                            for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
+                                matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
+                            }) {
+                                let method_name = impl_item_ref.ident.name;
+                                methods_in_trait.remove(&method_name);
+                                check_trait_method(method_name, impl_item_ref.span);
+                            }
+
+                            for method_name in methods_in_trait {
+                                check_trait_method(method_name, item.span);
+                            }
+                        },
+                        None => {
+                            for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
+                                matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
+                            }) {
+                                let method_name = impl_item_ref.ident.name;
+                                let impl_span = impl_item_ref.span;
+                                if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
+                                    span_lint_and_then(
+                                        cx,
+                                        SAME_NAME_METHOD,
+                                        impl_span,
+                                        "method's name is same to an existing method in a trait",
+                                        |diag| {
+                                            // TODO should we `span_note` on every trait?
+                                            // iterate on trait_spans?
+                                            diag.span_note(
+                                                trait_spans[0],
+                                                &format!("existing `{}` defined here", method_name),
+                                            );
+                                        },
+                                    );
+                                }
+                                existing_name.impl_methods.insert(method_name, impl_span);
+                            }
+                        },
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/types/box_collection.rs b/src/tools/clippy/clippy_lints/src/types/box_collection.rs
new file mode 100644 (file)
index 0000000..b28da29
--- /dev/null
@@ -0,0 +1,50 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_ty_param_diagnostic_item;
+use rustc_hir::{self as hir, def_id::DefId, QPath};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+
+use super::BOX_COLLECTION;
+
+pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
+    if_chain! {
+        if Some(def_id) == cx.tcx.lang_items().owned_box();
+        if let Some(item_type) = get_std_collection(cx, qpath);
+        then {
+            let generic = if item_type == "String" {
+                ""
+            } else {
+                "<..>"
+            };
+            span_lint_and_help(
+                cx,
+                BOX_COLLECTION,
+                hir_ty.span,
+                &format!(
+                    "you seem to be trying to use `Box<{outer}{generic}>`. Consider using just `{outer}{generic}`",
+                    outer=item_type,
+                    generic = generic),
+                None,
+                &format!(
+                    "`{outer}{generic}` is already on the heap, `Box<{outer}{generic}>` makes an extra allocation",
+                    outer=item_type,
+                    generic = generic)
+            );
+            true
+        } else {
+            false
+        }
+    }
+}
+
+fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
+    if is_ty_param_diagnostic_item(cx, qpath, sym::vec_type).is_some() {
+        Some("Vec")
+    } else if is_ty_param_diagnostic_item(cx, qpath, sym::string_type).is_some() {
+        Some("String")
+    } else if is_ty_param_diagnostic_item(cx, qpath, sym::hashmap_type).is_some() {
+        Some("HashMap")
+    } else {
+        None
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/types/box_vec.rs b/src/tools/clippy/clippy_lints/src/types/box_vec.rs
deleted file mode 100644 (file)
index d8b1953..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_ty_param_diagnostic_item;
-use rustc_hir::{self as hir, def_id::DefId, QPath};
-use rustc_lint::LateContext;
-use rustc_span::symbol::sym;
-
-use super::BOX_VEC;
-
-pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
-    if Some(def_id) == cx.tcx.lang_items().owned_box()
-        && is_ty_param_diagnostic_item(cx, qpath, sym::vec_type).is_some()
-    {
-        span_lint_and_help(
-            cx,
-            BOX_VEC,
-            hir_ty.span,
-            "you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`",
-            None,
-            "`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation",
-        );
-        true
-    } else {
-        false
-    }
-}
index 9588de8459cfe3f337b2351cc6afc5b6c6e8b800..bbe07db5358cdc805be57a209fc87c22871b24ee 100644 (file)
@@ -1,5 +1,5 @@
 mod borrowed_box;
-mod box_vec;
+mod box_collection;
 mod linked_list;
 mod option_option;
 mod rc_buffer;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for use of `Box<Vec<_>>` anywhere in the code.
+    /// Checks for use of `Box<T>` where T is a collection such as Vec anywhere in the code.
     /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
     ///
     /// ### Why is this bad?
-    /// `Vec` already keeps its contents in a separate area on
-    /// the heap. So if you `Box` it, you just add another level of indirection
+    /// Collections already keeps their contents in a separate area on
+    /// the heap. So if you `Box` them, you just add another level of indirection
     /// without any benefit whatsoever.
     ///
     /// ### Example
@@ -43,7 +43,7 @@
     ///     values: Vec<Foo>,
     /// }
     /// ```
-    pub BOX_VEC,
+    pub BOX_COLLECTION,
     perf,
     "usage of `Box<Vec<T>>`, vector elements are already on the heap"
 }
@@ -298,7 +298,7 @@ pub struct Types {
     avoid_breaking_exported_api: bool,
 }
 
-impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
+impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
 
 impl<'tcx> LateLintPass<'tcx> for Types {
     fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
@@ -447,7 +447,7 @@ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, mut context:
                         // in `clippy_lints::utils::conf.rs`
 
                         let mut triggered = false;
-                        triggered |= box_vec::check(cx, hir_ty, qpath, def_id);
+                        triggered |= box_collection::check(cx, hir_ty, qpath, def_id);
                         triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
                         triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
                         triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
index 8651fa6fcf9eaddeee67be294468d6b9afd573f9..1e0447239be99813cb7777c9e929fa600e86c71c 100644 (file)
@@ -15,6 +15,14 @@ pub struct Rename {
     pub rename: String,
 }
 
+/// A single disallowed method, used by the `DISALLOWED_METHOD` lint.
+#[derive(Clone, Debug, Deserialize)]
+#[serde(untagged)]
+pub enum DisallowedMethod {
+    Simple(String),
+    WithReason { path: String, reason: Option<String> },
+}
+
 /// Conf with parse errors
 #[derive(Default)]
 pub struct TryConf {
@@ -128,7 +136,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
 }
 
 define_Conf! {
-    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_VEC, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
+    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
@@ -243,7 +251,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
     /// Lint: DISALLOWED_METHOD.
     ///
     /// The list of disallowed methods, written as fully qualified paths.
-    (disallowed_methods: Vec<String> = Vec::new()),
+    (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
     /// Lint: DISALLOWED_TYPE.
     ///
     /// The list of disallowed types, written as fully qualified paths.
index 756c33d70c26d319769a023da4125fadd10c4a59..3e2a4e9748db6ae6079bae02204cbd69256cbf2e 100644 (file)
@@ -561,9 +561,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 
 impl EarlyLintPass for ProduceIce {
     fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
-        if is_trigger_fn(fn_kind) {
-            panic!("Would you like some help with that?");
-        }
+        assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
     }
 }
 
@@ -894,7 +892,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
                 }).collect();
             if !check_path(cx, &path[..]);
             then {
-                span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path");
+                span_lint(cx, INVALID_PATHS, item.span, "invalid path");
             }
         }
     }
@@ -1224,5 +1222,10 @@ fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: S
     let sm = cx.sess().source_map();
     let span = sm.span_extend_to_prev_str(span, "let", false);
     let span = sm.span_extend_to_next_char(span, ';', false);
-    Span::new(span.lo() - BytePos(3), span.hi() + BytePos(1), span.ctxt())
+    Span::new(
+        span.lo() - BytePos(3),
+        span.hi() + BytePos(1),
+        span.ctxt(),
+        span.parent(),
+    )
 }
index 188d0419c3993c9589b872ece99c6ee2e575a7e8..0d27874b7affb88d7a4dc16f119469919cbf23ef 100644 (file)
@@ -298,6 +298,7 @@ pub struct ClippyConfiguration {
     default: String,
     lints: Vec<String>,
     doc: String,
+    #[allow(dead_code)]
     deprecation_reason: Option<&'static str>,
 }
 
index 7c24e830e71dc406e2c081b77dcc09a47eb08d57..e7fca3ae5d401b8b3f84c4c71c340145a95d27cb 100644 (file)
@@ -1,7 +1,7 @@
 [package]
 name = "clippy_utils"
 version = "0.1.57"
-edition = "2018"
+edition = "2021"
 publish = false
 
 [dependencies]
index 133f6c29f7d2162d6300eca8c3d96d9f61af97d4..2fa98831c7740aa31e7765ddf463cb36eba3d658 100644 (file)
@@ -46,15 +46,12 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
         | (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
         | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
         (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
-        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
+        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
         (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
             eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
         },
         (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
-            lr == rr
-                && eq_maybe_qself(lqself, rqself)
-                && eq_path(lp, rp)
-                && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
+            lr == rr && eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat)
         },
         (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
         (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
@@ -76,7 +73,7 @@ pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool {
     l.is_placeholder == r.is_placeholder
         && eq_id(l.ident, r.ident)
         && eq_pat(&l.pat, &r.pat)
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        && over(&l.attrs, &r.attrs, eq_attr)
 }
 
 pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
@@ -92,7 +89,7 @@ pub fn eq_maybe_qself(l: &Option<QSelf>, r: &Option<QSelf>) -> bool {
 }
 
 pub fn eq_path(l: &Path, r: &Path) -> bool {
-    over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r))
+    over(&l.segments, &r.segments, eq_path_seg)
 }
 
 pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
@@ -101,9 +98,7 @@ pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
 
 pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
     match (l, r) {
-        (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => {
-            over(&l.args, &r.args, |l, r| eq_angle_arg(l, r))
-        },
+        (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg),
         (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
             over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
         },
@@ -142,7 +137,7 @@ pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool {
 
 pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
     use ExprKind::*;
-    if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) {
+    if !over(&l.attrs, &r.attrs, eq_attr) {
         return false;
     }
     match (&l.kind, &r.kind) {
@@ -173,20 +168,20 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
         (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
         (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
-        (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)),
+        (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
         (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => {
             lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb)
         },
         (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
         (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
         (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
-        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
+        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
         (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
         (Struct(lse), Struct(rse)) => {
             eq_maybe_qself(&lse.qself, &rse.qself)
                 && eq_path(&lse.path, &rse.path)
                 && eq_struct_rest(&lse.rest, &rse.rest)
-                && unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r))
+                && unordered_over(&lse.fields, &rse.fields, eq_field)
         },
         _ => false,
     }
@@ -196,7 +191,7 @@ pub fn eq_field(l: &ExprField, r: &ExprField) -> bool {
     l.is_placeholder == r.is_placeholder
         && eq_id(l.ident, r.ident)
         && eq_expr(&l.expr, &r.expr)
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        && over(&l.attrs, &r.attrs, eq_attr)
 }
 
 pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
@@ -204,7 +199,7 @@ pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
         && eq_pat(&l.pat, &r.pat)
         && eq_expr(&l.body, &r.body)
         && eq_expr_opt(&l.guard, &r.guard)
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        && over(&l.attrs, &r.attrs, eq_attr)
 }
 
 pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
@@ -212,7 +207,7 @@ pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
 }
 
 pub fn eq_block(l: &Block, r: &Block) -> bool {
-    l.rules == r.rules && over(&l.stmts, &r.stmts, |l, r| eq_stmt(l, r))
+    l.rules == r.rules && over(&l.stmts, &r.stmts, eq_stmt)
 }
 
 pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
@@ -222,13 +217,13 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
             eq_pat(&l.pat, &r.pat)
                 && both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
                 && eq_local_kind(&l.kind, &r.kind)
-                && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+                && over(&l.attrs, &r.attrs, eq_attr)
         },
         (Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
         (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
         (Empty, Empty) => true,
         (MacCall(l), MacCall(r)) => {
-            l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+            l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, eq_attr)
         },
         _ => false,
     }
@@ -245,10 +240,7 @@ pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool {
 }
 
 pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
-    eq_id(l.ident, r.ident)
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-        && eq_vis(&l.vis, &r.vis)
-        && eq_kind(&l.kind, &r.kind)
+    eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
 }
 
 pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
@@ -272,18 +264,15 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
                 }
         },
         (ForeignMod(l), ForeignMod(r)) => {
-            both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r))
-                && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
+            both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
         },
         (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
             eq_defaultness(*ld, *rd)
                 && eq_generics(lg, rg)
-                && over(lb, rb, |l, r| eq_generic_bound(l, r))
+                && over(lb, rb, eq_generic_bound)
                 && both(lt, rt, |l, r| eq_ty(l, r))
         },
-        (Enum(le, lg), Enum(re, rg)) => {
-            over(&le.variants, &re.variants, |l, r| eq_variant(l, r)) && eq_generics(lg, rg)
-        },
+        (Enum(le, lg), Enum(re, rg)) => over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg),
         (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
             eq_variant_data(lv, rv) && eq_generics(lg, rg)
         },
@@ -291,10 +280,10 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
             la == ra
                 && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
                 && eq_generics(lg, rg)
-                && over(lb, rb, |l, r| eq_generic_bound(l, r))
+                && over(lb, rb, eq_generic_bound)
                 && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
         },
-        (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, |l, r| eq_generic_bound(l, r)),
+        (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, eq_generic_bound),
         (
             Impl(box ImplKind {
                 unsafety: lu,
@@ -342,7 +331,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
         (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
             eq_defaultness(*ld, *rd)
                 && eq_generics(lg, rg)
-                && over(lb, rb, |l, r| eq_generic_bound(l, r))
+                && over(lb, rb, eq_generic_bound)
                 && both(lt, rt, |l, r| eq_ty(l, r))
         },
         (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
@@ -360,7 +349,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
         (TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
             eq_defaultness(*ld, *rd)
                 && eq_generics(lg, rg)
-                && over(lb, rb, |l, r| eq_generic_bound(l, r))
+                && over(lb, rb, eq_generic_bound)
                 && both(lt, rt, |l, r| eq_ty(l, r))
         },
         (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
@@ -370,7 +359,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
 
 pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
     l.is_placeholder == r.is_placeholder
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        && over(&l.attrs, &r.attrs, eq_attr)
         && eq_vis(&l.vis, &r.vis)
         && eq_id(l.ident, r.ident)
         && eq_variant_data(&l.data, &r.data)
@@ -381,14 +370,14 @@ pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
     use VariantData::*;
     match (l, r) {
         (Unit(_), Unit(_)) => true,
-        (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, |l, r| eq_struct_field(l, r)),
+        (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, eq_struct_field),
         _ => false,
     }
 }
 
 pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool {
     l.is_placeholder == r.is_placeholder
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        && over(&l.attrs, &r.attrs, eq_attr)
         && eq_vis(&l.vis, &r.vis)
         && both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
         && eq_ty(&l.ty, &r.ty)
@@ -406,7 +395,7 @@ pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
 }
 
 pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
-    over(&l.params, &r.params, |l, r| eq_generic_param(l, r))
+    over(&l.params, &r.params, eq_generic_param)
         && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
             eq_where_predicate(l, r)
         })
@@ -419,10 +408,10 @@ pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
             over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
                 eq_generic_param(l, r)
             }) && eq_ty(&l.bounded_ty, &r.bounded_ty)
-                && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
+                && over(&l.bounds, &r.bounds, eq_generic_bound)
         },
         (RegionPredicate(l), RegionPredicate(r)) => {
-            eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
+            eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, eq_generic_bound)
         },
         (EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
         _ => false,
@@ -469,7 +458,7 @@ pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
             l.is_placeholder == r.is_placeholder
                 && eq_pat(&l.pat, &r.pat)
                 && eq_ty(&l.ty, &r.ty)
-                && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+                && over(&l.attrs, &r.attrs, eq_attr)
         })
 }
 
@@ -496,13 +485,13 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
         (BareFn(l), BareFn(r)) => {
             l.unsafety == r.unsafety
                 && eq_ext(&l.ext, &r.ext)
-                && over(&l.generic_params, &r.generic_params, |l, r| eq_generic_param(l, r))
+                && over(&l.generic_params, &r.generic_params, eq_generic_param)
                 && eq_fn_decl(&l.decl, &r.decl)
         },
         (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
-        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
-        (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, |l, r| eq_generic_bound(l, r)),
-        (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, |l, r| eq_generic_bound(l, r)),
+        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
+        (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
+        (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound),
         (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
         (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
         _ => false,
@@ -533,7 +522,7 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
     use GenericParamKind::*;
     l.is_placeholder == r.is_placeholder
         && eq_id(l.ident, r.ident)
-        && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
+        && over(&l.bounds, &r.bounds, eq_generic_bound)
         && match (&l.kind, &r.kind) {
             (Lifetime, Lifetime) => true,
             (Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
@@ -548,10 +537,10 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
                     kw_span: _,
                     default: rd,
                 },
-            ) => eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)),
+            ) => eq_ty(lt, rt) && both(ld, rd, eq_anon_const),
             _ => false,
         }
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+        && over(&l.attrs, &r.attrs, eq_attr)
 }
 
 pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
@@ -568,7 +557,7 @@ pub fn eq_assoc_constraint(l: &AssocTyConstraint, r: &AssocTyConstraint) -> bool
     eq_id(l.ident, r.ident)
         && match (&l.kind, &r.kind) {
             (Equality { ty: l }, Equality { ty: r }) => eq_ty(l, r),
-            (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, |l, r| eq_generic_bound(l, r)),
+            (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, eq_generic_bound),
             _ => false,
         }
 }
index 29e2559fc6d603d45a05b8d0e91b142095f23f57..9650294fc7b879f93450bbf5da74bc64da5abda2 100644 (file)
@@ -27,10 +27,9 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
     match expr.kind {
         ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
         ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
-        ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)),
+        ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(identify_some_pure_patterns),
         ExprKind::Struct(_, fields, expr) => {
-            fields.iter().all(|f| identify_some_pure_patterns(f.expr))
-                && expr.map_or(true, |e| identify_some_pure_patterns(e))
+            fields.iter().all(|f| identify_some_pure_patterns(f.expr)) && expr.map_or(true, identify_some_pure_patterns)
         },
         ExprKind::Call(
             &Expr {
@@ -45,7 +44,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
                 ..
             },
             args,
-        ) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
+        ) => args.iter().all(identify_some_pure_patterns),
         ExprKind::Block(
             &Block {
                 stmts,
index e6c062249994f85c37266b85fc54eb9b0b72f405..ba4d50bf74469307cbfedc51e1c979c532c74d4e 100644 (file)
@@ -602,3 +602,33 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
 
     false
 }
+
+/// A parsed `panic!` expansion
+pub struct PanicExpn<'tcx> {
+    /// Span of `panic!(..)`
+    pub call_site: Span,
+    /// Inner `format_args!` expansion
+    pub format_args: FormatArgsExpn<'tcx>,
+}
+
+impl PanicExpn<'tcx> {
+    /// Parses an expanded `panic!` invocation
+    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
+        if_chain! {
+            if let ExprKind::Block(block, _) = expr.kind;
+            if let Some(init) = block.expr;
+            if let ExprKind::Call(_, [format_args]) = init.kind;
+            let expn_data = expr.span.ctxt().outer_expn_data();
+            if let ExprKind::AddrOf(_, _, format_args) = format_args.kind;
+            if let Some(format_args) = FormatArgsExpn::parse(format_args);
+            then {
+                Some(PanicExpn {
+                    call_site: expn_data.call_site,
+                    format_args,
+                })
+            } else {
+                None
+            }
+        }
+    }
+}
index 6e9a1de21eef5cb010d32f668a82565ba1713a35..7438b6eabf9e61f267670b1c3df24b4018414ebd 100644 (file)
@@ -540,7 +540,7 @@ pub fn hash_block(&mut self, b: &Block<'_>) {
         std::mem::discriminant(&b.rules).hash(&mut self.s);
     }
 
-    #[allow(clippy::many_single_char_names, clippy::too_many_lines)]
+    #[allow(clippy::too_many_lines)]
     pub fn hash_expr(&mut self, e: &Expr<'_>) {
         let simple_const = self
             .maybe_typeck_results
index 80be4350c3c1a5027bba117e3d78ad09e4d4f9c5..7a8208c12c0d1e3226923ddaffcd2bc128e9c3c5 100644 (file)
@@ -68,6 +68,7 @@
 pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
 pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
 pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
+#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 #[cfg(feature = "internal-lints")]
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
index e2f2e2008bb2604543f1ba4814b0d92538d89fc1..238728f090f56aba3b5500f3545ee8177f0570d6 100644 (file)
@@ -192,9 +192,8 @@ fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rv
                 ))
             }
         },
-        Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => Ok(()),
+        Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
         Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
-        Rvalue::ShallowInitBox(_, _) => Ok(()),
         Rvalue::UnaryOp(_, operand) => {
             let ty = operand.ty(body, tcx);
             if ty.is_integral() || ty.is_bool() {
index f1e03ba42966d9bd31e3597f6d00ef0e0e1fe7b3..97d3794fb84f0a951de6acd2e8a0e3eced02d357 100644 (file)
@@ -15,6 +15,8 @@
     env, fmt,
     fs::write,
     path::{Path, PathBuf},
+    thread,
+    time::Duration,
 };
 
 use clap::{App, Arg, ArgMatches};
@@ -109,6 +111,22 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
+fn get(path: &str) -> Result<ureq::Response, ureq::Error> {
+    const MAX_RETRIES: u8 = 4;
+    let mut retries = 0;
+    loop {
+        match ureq::get(path).call() {
+            Ok(res) => return Ok(res),
+            Err(e) if retries >= MAX_RETRIES => return Err(e),
+            Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e),
+            Err(e) => return Err(e),
+        }
+        eprintln!("retrying in {} seconds...", retries);
+        thread::sleep(Duration::from_secs(retries as u64));
+        retries += 1;
+    }
+}
+
 impl CrateSource {
     /// Makes the sources available on the disk for clippy to check.
     /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
@@ -129,7 +147,7 @@ fn download_and_extract(&self) -> Crate {
                 if !krate_file_path.is_file() {
                     // create a file path to download and write the crate data into
                     let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
-                    let mut krate_req = ureq::get(&url).call().unwrap().into_reader();
+                    let mut krate_req = get(&url).unwrap().into_reader();
                     // copy the crate into the file
                     std::io::copy(&mut krate_req, &mut krate_dest).unwrap();
 
index 92bde3423a20a811c3c05cf0294f4b62c04bcc35..660401ff28c5de4ac6f38defee3da72e3cc48575 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-09-08"
+channel = "nightly-2021-09-28"
 components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
index 7589f42a7b4d3386f951839bdcc2d4807d852656..7ebdd947893e9e1570ed1fe8d31ba32b2b4ffefd 100644 (file)
@@ -4,7 +4,6 @@
 
 use rustc_tools_util::VersionInfo;
 use std::env;
-use std::ffi::OsString;
 use std::path::PathBuf;
 use std::process::{self, Command};
 
@@ -14,7 +13,7 @@
     cargo clippy [options] [--] [<opts>...]
 
 Common options:
-    --no-deps                Run Clippy only on the given crate, without linting the dependencies 
+    --no-deps                Run Clippy only on the given crate, without linting the dependencies
     --fix                    Automatically apply lint suggestions. This flag implies `--no-deps`
     -h, --help               Print this message
     -V, --version            Print version info and exit
@@ -116,22 +115,6 @@ fn path() -> PathBuf {
         path
     }
 
-    fn target_dir() -> Option<(&'static str, OsString)> {
-        env::var_os("CLIPPY_DOGFOOD")
-            .map(|_| {
-                env::var_os("CARGO_MANIFEST_DIR").map_or_else(
-                    || std::ffi::OsString::from("clippy_dogfood"),
-                    |d| {
-                        std::path::PathBuf::from(d)
-                            .join("target")
-                            .join("dogfood")
-                            .into_os_string()
-                    },
-                )
-            })
-            .map(|p| ("CARGO_TARGET_DIR", p))
-    }
-
     fn into_std_cmd(self) -> Command {
         let mut cmd = Command::new("cargo");
         let clippy_args: String = self
@@ -141,7 +124,6 @@ fn into_std_cmd(self) -> Command {
             .collect();
 
         cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path())
-            .envs(ClippyCmd::target_dir())
             .env("CLIPPY_ARGS", clippy_args)
             .arg(self.cargo_subcommand)
             .args(&self.args);
index a8f3e3145f64d723de26b9729b5375c21fd28a0b..4dbe71e4b6ad6ae032be2c43649c6deba8aecf37 100644 (file)
@@ -1,25 +1,3 @@
-use std::env;
-use std::lazy::SyncLazy;
-use std::path::PathBuf;
-
-pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
-    Some(v) => v.into(),
-    None => env::current_dir().unwrap().join("target"),
-});
-
-pub static TARGET_LIB: SyncLazy<PathBuf> = SyncLazy::new(|| {
-    if let Some(path) = option_env!("TARGET_LIBS") {
-        path.into()
-    } else {
-        let mut dir = CARGO_TARGET_DIR.clone();
-        if let Some(target) = env::var_os("CARGO_BUILD_TARGET") {
-            dir.push(target);
-        }
-        dir.push(env!("PROFILE"));
-        dir
-    }
-});
-
 #[must_use]
 pub fn is_rustc_test_suite() -> bool {
     option_env!("RUSTC_TEST_SUITE").is_some()
index c63c47690d52ad9e9027ecfa6058217e09eae70f..d7596f6ff0cae9e7e78f9c72b438ebb55da6e2e5 100644 (file)
@@ -1,5 +1,4 @@
 #![feature(test)] // compiletest_rs requires this attribute
-#![feature(once_cell)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 #[allow(unused_extern_crates)]
 extern crate syn;
 
-fn host_lib() -> PathBuf {
-    option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
-}
-
-fn clippy_driver_path() -> PathBuf {
-    option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
-}
-
 /// Produces a string with an `--extern` flag for all UI test crate
 /// dependencies.
 ///
@@ -99,12 +90,14 @@ fn extern_flags() -> String {
         .copied()
         .filter(|n| !crates.contains_key(n))
         .collect();
-    if !not_found.is_empty() {
-        panic!("dependencies not found in depinfo: {:?}", not_found);
-    }
+    assert!(
+        not_found.is_empty(),
+        "dependencies not found in depinfo: {:?}",
+        not_found
+    );
     crates
         .into_iter()
-        .map(|(name, path)| format!("--extern {}={} ", name, path))
+        .map(|(name, path)| format!(" --extern {}={}", name, path))
         .collect()
 }
 
@@ -120,19 +113,29 @@ fn default_config() -> compiletest::Config {
         config.run_lib_path = path.clone();
         config.compile_lib_path = path;
     }
+    let current_exe_path = std::env::current_exe().unwrap();
+    let deps_path = current_exe_path.parent().unwrap();
+    let profile_path = deps_path.parent().unwrap();
 
     // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
     // This is valuable because a) it allows us to monitor what external dependencies are used
     // and b) it ensures that conflicting rlibs are resolved properly.
+    let host_libs = option_env!("HOST_LIBS")
+        .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
+        .unwrap_or_default();
     config.target_rustcflags = Some(format!(
-        "--emit=metadata -L dependency={} -L dependency={} -Dwarnings -Zui-testing {}",
-        host_lib().join("deps").display(),
-        cargo::TARGET_LIB.join("deps").display(),
+        "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
+        deps_path.display(),
+        host_libs,
         extern_flags(),
     ));
 
-    config.build_base = host_lib().join("test_build_base");
-    config.rustc_path = clippy_driver_path();
+    config.build_base = profile_path.join("test");
+    config.rustc_path = profile_path.join(if cfg!(windows) {
+        "clippy-driver.exe"
+    } else {
+        "clippy-driver"
+    });
     config
 }
 
index 54f452172deb4a3f727e3a586fbd8f3266c45efe..a37cdfed126f6cea9c46d063abf01ac7ff801e5f 100644 (file)
 
 mod cargo;
 
-static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy"));
+static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| {
+    let mut path = std::env::current_exe().unwrap();
+    assert!(path.pop()); // deps
+    path.set_file_name("cargo-clippy");
+    path
+});
 
 #[test]
 fn dogfood_clippy() {
@@ -28,7 +33,6 @@ fn dogfood_clippy() {
     let mut command = Command::new(&*CLIPPY_PATH);
     command
         .current_dir(root_dir)
-        .env("CLIPPY_DOGFOOD", "1")
         .env("CARGO_INCREMENTAL", "0")
         .arg("clippy")
         .arg("--all-targets")
@@ -74,7 +78,6 @@ fn test_no_deps_ignores_path_deps_in_workspaces() {
     // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
     let output = Command::new(&*CLIPPY_PATH)
         .current_dir(&cwd)
-        .env("CLIPPY_DOGFOOD", "1")
         .env("CARGO_INCREMENTAL", "0")
         .arg("clippy")
         .args(&["-p", "subcrate"])
@@ -94,7 +97,6 @@ fn test_no_deps_ignores_path_deps_in_workspaces() {
         // Test that without the `--no-deps` argument, `path_dep` is linted.
         let output = Command::new(&*CLIPPY_PATH)
             .current_dir(&cwd)
-            .env("CLIPPY_DOGFOOD", "1")
             .env("CARGO_INCREMENTAL", "0")
             .arg("clippy")
             .args(&["-p", "subcrate"])
@@ -121,7 +123,6 @@ fn test_no_deps_ignores_path_deps_in_workspaces() {
     let successful_build = || {
         let output = Command::new(&*CLIPPY_PATH)
             .current_dir(&cwd)
-            .env("CLIPPY_DOGFOOD", "1")
             .env("CARGO_INCREMENTAL", "0")
             .arg("clippy")
             .args(&["-p", "subcrate"])
@@ -223,7 +224,6 @@ fn run_clippy_for_project(project: &str) {
 
     command
         .current_dir(root_dir.join(project))
-        .env("CLIPPY_DOGFOOD", "1")
         .env("CARGO_INCREMENTAL", "0")
         .arg("clippy")
         .arg("--all-targets")
index 7e3eff3c7324fc3b4f31b1d4443b576e3cd41883..c64425fa01a42a36b8351e934ae81523d75b7129 100644 (file)
@@ -74,8 +74,11 @@ fn integration_test() {
         panic!("incompatible crate versions");
     } else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
         panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
-    } else if stderr.contains("toolchain") && stderr.contains("is not installed") {
-        panic!("missing required toolchain");
+    } else {
+        assert!(
+            !stderr.contains("toolchain") || !stderr.contains("is not installed"),
+            "missing required toolchain"
+        );
     }
 
     match output.status.code() {
index bd69d661b714fe8bd29e553588d2736e3eb22fc3..20aa81b98a080c1b01478d2466910e4ceb5c9a5c 100644 (file)
@@ -4,7 +4,7 @@ error: invalid path
 LL |     pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `-D clippy::clippy-lints-internal` implied by `-D warnings`
+   = note: `-D clippy::invalid-paths` implied by `-D warnings`
 
 error: invalid path
   --> $DIR/invalid_paths.rs:20:5
index a3245da68250c3d28939ddb5da02dc08fbb938ce..f1d4a4619c5dc995105fd11715f4d422e5c4bec9 100644 (file)
@@ -1,5 +1,8 @@
 disallowed-methods = [
+    # just a string is shorthand for path only
     "std::iter::Iterator::sum",
-    "regex::Regex::is_match",
-    "regex::Regex::new"
+    # can give path and reason with an inline table
+    { path = "regex::Regex::is_match", reason = "no matching allowed" },
+    # can use an inline table but omit reason
+    { path = "regex::Regex::new" },
 ]
index 2b628c67fa75162127adcbb922826c60966017fb..38123220a4320e5ceb5dd0137cc4d08b3e761d89 100644 (file)
@@ -1,4 +1,4 @@
-error: use of a disallowed method `regex::re_unicode::Regex::new`
+error: use of a disallowed method `regex::Regex::new`
   --> $DIR/conf_disallowed_method.rs:7:14
    |
 LL |     let re = Regex::new(r"ab.*c").unwrap();
@@ -6,13 +6,15 @@ LL |     let re = Regex::new(r"ab.*c").unwrap();
    |
    = note: `-D clippy::disallowed-method` implied by `-D warnings`
 
-error: use of a disallowed method `regex::re_unicode::Regex::is_match`
+error: use of a disallowed method `regex::Regex::is_match`
   --> $DIR/conf_disallowed_method.rs:8:5
    |
 LL |     re.is_match("abc");
    |     ^^^^^^^^^^^^^^^^^^
+   |
+   = note: no matching allowed (from clippy.toml)
 
-error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum`
+error: use of a disallowed method `std::iter::Iterator::sum`
   --> $DIR/conf_disallowed_method.rs:11:5
    |
 LL |     a.iter().sum::<i32>();
index 19019a2541631767abf56555c0411d03465af1af..fb0e226f3aa40b0ed32d63646b4e1b494740ac24 100644 (file)
@@ -2,7 +2,6 @@
 // normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
 
 #![deny(clippy::trivially_copy_pass_by_ref)]
-#![allow(clippy::many_single_char_names)]
 
 #[derive(Copy, Clone)]
 struct Foo(u8);
index 912761a8f009c2dfd2e856d7e62d12ad5113e94c..b3ef5928e8ea22302017c8ea3c2d79e238916570 100644 (file)
@@ -1,5 +1,5 @@
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/test.rs:15:11
+  --> $DIR/test.rs:14:11
    |
 LL | fn bad(x: &u16, y: &Foo) {}
    |           ^^^^ help: consider passing by value instead: `u16`
@@ -11,7 +11,7 @@ LL | #![deny(clippy::trivially_copy_pass_by_ref)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/test.rs:15:20
+  --> $DIR/test.rs:14:20
    |
 LL | fn bad(x: &u16, y: &Foo) {}
    |                    ^^^^ help: consider passing by value instead: `Foo`
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs
new file mode 100644 (file)
index 0000000..26c8848
--- /dev/null
@@ -0,0 +1,75 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree};
+use std::iter::FromIterator;
+
+fn read_ident(iter: &mut token_stream::IntoIter) -> Ident {
+    match iter.next() {
+        Some(TokenTree::Ident(i)) => i,
+        _ => panic!("expected ident"),
+    }
+}
+
+#[proc_macro_derive(DeriveBadSpan)]
+pub fn derive_bad_span(input: TokenStream) -> TokenStream {
+    let mut input = input.into_iter();
+    assert_eq!(read_ident(&mut input).to_string(), "struct");
+    let ident = read_ident(&mut input);
+    let mut tys = match input.next() {
+        Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => g.stream().into_iter(),
+        _ => panic!(),
+    };
+    let field1 = read_ident(&mut tys);
+    tys.next();
+    let field2 = read_ident(&mut tys);
+
+    <TokenStream as FromIterator<TokenTree>>::from_iter(
+        [
+            Ident::new("impl", Span::call_site()).into(),
+            ident.into(),
+            Group::new(
+                Delimiter::Brace,
+                <TokenStream as FromIterator<TokenTree>>::from_iter(
+                    [
+                        Ident::new("fn", Span::call_site()).into(),
+                        Ident::new("_foo", Span::call_site()).into(),
+                        Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
+                        Group::new(
+                            Delimiter::Brace,
+                            <TokenStream as FromIterator<TokenTree>>::from_iter(
+                                [
+                                    Ident::new("if", field1.span()).into(),
+                                    Ident::new("true", field1.span()).into(),
+                                    {
+                                        let mut group = Group::new(Delimiter::Brace, TokenStream::new());
+                                        group.set_span(field1.span());
+                                        group.into()
+                                    },
+                                    Ident::new("if", field2.span()).into(),
+                                    Ident::new("true", field2.span()).into(),
+                                    {
+                                        let mut group = Group::new(Delimiter::Brace, TokenStream::new());
+                                        group.set_span(field2.span());
+                                        group.into()
+                                    },
+                                ]
+                                .iter()
+                                .cloned(),
+                            ),
+                        )
+                        .into(),
+                    ]
+                    .iter()
+                    .cloned(),
+                ),
+            )
+            .into(),
+        ]
+        .iter()
+        .cloned(),
+    )
+}
diff --git a/src/tools/clippy/tests/ui/box_collection.rs b/src/tools/clippy/tests/ui/box_collection.rs
new file mode 100644 (file)
index 0000000..e00f061
--- /dev/null
@@ -0,0 +1,44 @@
+#![warn(clippy::all)]
+#![allow(
+    clippy::boxed_local,
+    clippy::needless_pass_by_value,
+    clippy::blacklisted_name,
+    unused
+)]
+
+use std::collections::HashMap;
+
+macro_rules! boxit {
+    ($init:expr, $x:ty) => {
+        let _: Box<$x> = Box::new($init);
+    };
+}
+
+fn test_macro() {
+    boxit!(Vec::new(), Vec<u8>);
+}
+
+fn test(foo: Box<Vec<bool>>) {}
+
+fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
+    // pass if #31 is fixed
+    foo(vec![1, 2, 3])
+}
+
+fn test3(foo: Box<String>) {}
+
+fn test4(foo: Box<HashMap<String, String>>) {}
+
+fn test_local_not_linted() {
+    let _: Box<Vec<bool>>;
+}
+
+// All of these test should be allowed because they are part of the
+// public api and `avoid_breaking_exported_api` is `false` by default.
+pub fn pub_test(foo: Box<Vec<bool>>) {}
+
+pub fn pub_test_ret() -> Box<Vec<bool>> {
+    Box::new(Vec::new())
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/box_collection.stderr b/src/tools/clippy/tests/ui/box_collection.stderr
new file mode 100644 (file)
index 0000000..6de85d0
--- /dev/null
@@ -0,0 +1,27 @@
+error: you seem to be trying to use `Box<Vec<..>>`. Consider using just `Vec<..>`
+  --> $DIR/box_collection.rs:21:14
+   |
+LL | fn test(foo: Box<Vec<bool>>) {}
+   |              ^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::box-collection` implied by `-D warnings`
+   = help: `Vec<..>` is already on the heap, `Box<Vec<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<String>`. Consider using just `String`
+  --> $DIR/box_collection.rs:28:15
+   |
+LL | fn test3(foo: Box<String>) {}
+   |               ^^^^^^^^^^^
+   |
+   = help: `String` is already on the heap, `Box<String>` makes an extra allocation
+
+error: you seem to be trying to use `Box<HashMap<..>>`. Consider using just `HashMap<..>`
+  --> $DIR/box_collection.rs:30:15
+   |
+LL | fn test4(foo: Box<HashMap<String, String>>) {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: `HashMap<..>` is already on the heap, `Box<HashMap<..>>` makes an extra allocation
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/box_vec.rs b/src/tools/clippy/tests/ui/box_vec.rs
deleted file mode 100644 (file)
index 1d63669..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#![warn(clippy::all)]
-#![allow(
-    clippy::boxed_local,
-    clippy::needless_pass_by_value,
-    clippy::blacklisted_name,
-    unused
-)]
-
-macro_rules! boxit {
-    ($init:expr, $x:ty) => {
-        let _: Box<$x> = Box::new($init);
-    };
-}
-
-fn test_macro() {
-    boxit!(Vec::new(), Vec<u8>);
-}
-fn test(foo: Box<Vec<bool>>) {}
-
-fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
-    // pass if #31 is fixed
-    foo(vec![1, 2, 3])
-}
-
-fn test_local_not_linted() {
-    let _: Box<Vec<bool>>;
-}
-
-// All of these test should be allowed because they are part of the
-// public api and `avoid_breaking_exported_api` is `false` by default.
-pub fn pub_test(foo: Box<Vec<bool>>) {}
-pub fn pub_test_ret() -> Box<Vec<bool>> {
-    Box::new(Vec::new())
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/box_vec.stderr b/src/tools/clippy/tests/ui/box_vec.stderr
deleted file mode 100644 (file)
index 58c1f13..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-error: you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`
-  --> $DIR/box_vec.rs:18:14
-   |
-LL | fn test(foo: Box<Vec<bool>>) {}
-   |              ^^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::box-vec` implied by `-D warnings`
-   = help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation
-
-error: aborting due to previous error
-
index cba7666c2d8a7c9b8039fe08c7a0f429f49420d2..1ed78547a60cd49a40f20493f9fcf964a3bc695c 100644 (file)
@@ -15,11 +15,12 @@ pub trait Copy {}
 pub unsafe trait Freeze {}
 
 #[lang = "start"]
-#[start]
-fn start(_argc: isize, _argv: *const *const u8) -> isize {
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
     0
 }
 
+fn main() {}
+
 struct A;
 
 impl A {
index 702684f6b43a6467067f02a9780df538435f81bf..6210d7c6cfd80c1d32369494f3010694b606dc65 100644 (file)
@@ -1,5 +1,5 @@
 error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
-  --> $DIR/def_id_nocore.rs:26:19
+  --> $DIR/def_id_nocore.rs:27:19
    |
 LL |     pub fn as_ref(self) -> &'static str {
    |                   ^^^^
index f1f9c123dc842880683a14584d5c63f7bb7aa9c1..9114d8754dcc8f4b0ad598aa2b0faaaeecd882fb 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![allow(unused_imports,dead_code)]
+#![allow(unused_imports, dead_code)]
 #![deny(clippy::default_trait_access)]
 
 use std::default;
index 7f3dfc7f01366e0d39b23d59350a2428fe6b0e7c..8a5f0d6a74976665c8181638cde258c82ab50716 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![allow(unused_imports,dead_code)]
+#![allow(unused_imports, dead_code)]
 #![deny(clippy::default_trait_access)]
 
 use std::default;
index 0795900558b695824bdead9959b7201313b13034..d4832daa6895970746de537909bb6339af07331e 100644 (file)
@@ -9,7 +9,7 @@ fn get_reference(n: &usize) -> &usize {
     n
 }
 
-#[allow(clippy::many_single_char_names, clippy::double_parens)]
+#[allow(clippy::double_parens)]
 #[allow(unused_variables, unused_parens)]
 fn main() {
     let a = 10;
index 60c4318601bc881799a07d7e6e7ffdcbd3886957..be7cc669b5b6daf3c0cb855d6fe861e7920e943e 100644 (file)
@@ -9,7 +9,7 @@ fn get_reference(n: &usize) -> &usize {
     n
 }
 
-#[allow(clippy::many_single_char_names, clippy::double_parens)]
+#[allow(clippy::double_parens)]
 #[allow(unused_variables, unused_parens)]
 fn main() {
     let a = 10;
index 336a743de726bf009387beaf5982b7a5c5a37285..ebbc0c77e32656caae3a17245d8042c2de0f6af3 100644 (file)
@@ -167,4 +167,44 @@ fn default() -> Self {
     }
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/7655
+
+pub struct SpecializedImpl2<T> {
+    v: Vec<T>,
+}
+
+impl Default for SpecializedImpl2<String> {
+    fn default() -> Self {
+        Self { v: Vec::new() }
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7654
+
+pub struct Color {
+    pub r: u8,
+    pub g: u8,
+    pub b: u8,
+}
+
+/// `#000000`
+impl Default for Color {
+    fn default() -> Self {
+        Color { r: 0, g: 0, b: 0 }
+    }
+}
+
+pub struct Color2 {
+    pub r: u8,
+    pub g: u8,
+    pub b: u8,
+}
+
+impl Default for Color2 {
+    /// `#000000`
+    fn default() -> Self {
+        Self { r: 0, g: 0, b: 0 }
+    }
+}
+
 fn main() {}
index 7ab23320db6d1b7aba040e382ea317cb517b2d2c..707b449f82e4f04ea4e85b4efeb76b17820aaa33 100644 (file)
@@ -2,7 +2,7 @@
 
 #[rustfmt::skip]
 #[warn(clippy::eq_op)]
-#[allow(clippy::identity_op, clippy::double_parens, clippy::many_single_char_names)]
+#[allow(clippy::identity_op, clippy::double_parens)]
 #[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
 #[allow(clippy::nonminimal_bool)]
 #[allow(unused)]
index 91b837f9a85884d19cc74471d262c1016987cf79..1de79667f55fb51132117565b5197338612f4b73 100644 (file)
@@ -4,7 +4,6 @@
     unused,
     clippy::no_effect,
     clippy::redundant_closure_call,
-    clippy::many_single_char_names,
     clippy::needless_pass_by_value,
     clippy::option_map_unit_fn
 )]
@@ -14,7 +13,7 @@
     clippy::needless_borrow
 )]
 
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 
 macro_rules! mac {
     () => {
@@ -30,19 +29,18 @@ macro_rules! closure_mac {
 
 fn main() {
     let a = Some(1u8).map(foo);
-    meta(foo);
     let c = Some(1u8).map(|a| {1+2; foo}(a));
     true.then(|| mac!()); // don't lint function in macro expansion
     Some(1).map(closure_mac!()); // don't lint closure in macro expansion
     let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec!
-    let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
-    all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted
+    let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted?
+    all(&[1, 2, 3], &2, below); //is adjusted
     unsafe {
         Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
     }
 
     // See #815
-    let e = Some(1u8).map(|a| divergent(a));
+    let e = Some(1u8).map(divergent);
     let e = Some(1u8).map(generic);
     let e = Some(1u8).map(generic);
     // See #515
@@ -90,24 +88,17 @@ impl<'a> std::ops::Deref for TestStruct<'a> {
 fn test_redundant_closures_containing_method_calls() {
     let i = 10;
     let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
-    let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
     let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
     let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
-    let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
-    let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
     let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
     unsafe {
         let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
     }
     let e = Some("str").map(std::string::ToString::to_string);
-    let e = Some("str").map(str::to_string);
-    let e = Some('a').map(char::to_uppercase);
     let e = Some('a').map(char::to_uppercase);
     let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
     let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
-    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
-    let p = Some(PathBuf::new());
-    let e = p.as_ref().and_then(|s| s.to_str());
+    let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
     let c = Some(TestStruct { some_ref: &i })
         .as_ref()
         .map(|c| c.to_ascii_uppercase());
@@ -119,10 +110,6 @@ fn test_redundant_closures_containing_method_calls() {
         t.iter().filter(|x| x.trait_foo_ref());
         t.iter().map(|x| x.trait_foo_ref());
     }
-
-    let mut some = Some(|x| x * x);
-    let arr = [Ok(1), Err(2)];
-    let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
 }
 
 struct Thunk<T>(Box<dyn FnMut() -> T>);
@@ -145,13 +132,6 @@ fn foobar() {
     thunk.unwrap()
 }
 
-fn meta<F>(f: F)
-where
-    F: Fn(u8),
-{
-    f(1u8)
-}
-
 fn foo(_: u8) {}
 
 fn foo2(_: u8) -> u8 {
@@ -180,7 +160,7 @@ fn generic<T>(_: T) -> u8 {
 }
 
 fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
-    requires_fn_once(|| x());
+    requires_fn_once(x);
 }
 fn requires_fn_once<T: FnOnce()>(_: T) {}
 
@@ -236,3 +216,35 @@ fn mutable_closure_in_loop() {
         Some(1).map(&mut closure);
     }
 }
+
+fn late_bound_lifetimes() {
+    fn take_asref_path<P: AsRef<Path>>(path: P) {}
+
+    fn map_str<F>(thunk: F)
+    where
+        F: FnOnce(&str),
+    {
+    }
+
+    fn map_str_to_path<F>(thunk: F)
+    where
+        F: FnOnce(&str) -> &Path,
+    {
+    }
+    map_str(|s| take_asref_path(s));
+    map_str_to_path(std::convert::AsRef::as_ref);
+}
+
+mod type_param_bound {
+    trait Trait {
+        fn fun();
+    }
+
+    fn take<T: 'static>(_: T) {}
+
+    fn test<X: Trait>() {
+        // don't lint, but it's questionable that rust requires a cast
+        take(|| X::fun());
+        take(X::fun as fn());
+    }
+}
index 1b53700289db303678d57850d0b59eb7a17b998d..86abd347baa7885645fed4ebefe3f7d2d985d1ce 100644 (file)
@@ -4,7 +4,6 @@
     unused,
     clippy::no_effect,
     clippy::redundant_closure_call,
-    clippy::many_single_char_names,
     clippy::needless_pass_by_value,
     clippy::option_map_unit_fn
 )]
@@ -14,7 +13,7 @@
     clippy::needless_borrow
 )]
 
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 
 macro_rules! mac {
     () => {
@@ -30,7 +29,6 @@ macro_rules! closure_mac {
 
 fn main() {
     let a = Some(1u8).map(|a| foo(a));
-    meta(|a| foo(a));
     let c = Some(1u8).map(|a| {1+2; foo}(a));
     true.then(|| mac!()); // don't lint function in macro expansion
     Some(1).map(closure_mac!()); // don't lint closure in macro expansion
@@ -90,24 +88,17 @@ fn deref(&self) -> &char {
 fn test_redundant_closures_containing_method_calls() {
     let i = 10;
     let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
-    let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
     let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
     let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
-    let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
     let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
-    let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
     unsafe {
         let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
     }
     let e = Some("str").map(|s| s.to_string());
-    let e = Some("str").map(str::to_string);
     let e = Some('a').map(|s| s.to_uppercase());
-    let e = Some('a').map(char::to_uppercase);
     let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
     let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
-    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
-    let p = Some(PathBuf::new());
-    let e = p.as_ref().and_then(|s| s.to_str());
+    let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
     let c = Some(TestStruct { some_ref: &i })
         .as_ref()
         .map(|c| c.to_ascii_uppercase());
@@ -119,10 +110,6 @@ fn test_different_borrow_levels<T>(t: &[&T])
         t.iter().filter(|x| x.trait_foo_ref());
         t.iter().map(|x| x.trait_foo_ref());
     }
-
-    let mut some = Some(|x| x * x);
-    let arr = [Ok(1), Err(2)];
-    let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
 }
 
 struct Thunk<T>(Box<dyn FnMut() -> T>);
@@ -145,13 +132,6 @@ fn foobar() {
     thunk.unwrap()
 }
 
-fn meta<F>(f: F)
-where
-    F: Fn(u8),
-{
-    f(1u8)
-}
-
 fn foo(_: u8) {}
 
 fn foo2(_: u8) -> u8 {
@@ -236,3 +216,35 @@ fn mutable_closure_in_loop() {
         Some(1).map(|n| closure(n));
     }
 }
+
+fn late_bound_lifetimes() {
+    fn take_asref_path<P: AsRef<Path>>(path: P) {}
+
+    fn map_str<F>(thunk: F)
+    where
+        F: FnOnce(&str),
+    {
+    }
+
+    fn map_str_to_path<F>(thunk: F)
+    where
+        F: FnOnce(&str) -> &Path,
+    {
+    }
+    map_str(|s| take_asref_path(s));
+    map_str_to_path(|s| s.as_ref());
+}
+
+mod type_param_bound {
+    trait Trait {
+        fn fun();
+    }
+
+    fn take<T: 'static>(_: T) {}
+
+    fn test<X: Trait>() {
+        // don't lint, but it's questionable that rust requires a cast
+        take(|| X::fun());
+        take(X::fun as fn());
+    }
+}
index 28da8941346192d0a6e6c61c478a1cc57bb5415d..8092f04c3fc3d28215844fdb850e19196da77bd3 100644 (file)
@@ -1,5 +1,5 @@
 error: redundant closure
-  --> $DIR/eta.rs:32:27
+  --> $DIR/eta.rs:31:27
    |
 LL |     let a = Some(1u8).map(|a| foo(a));
    |                           ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
@@ -7,19 +7,19 @@ LL |     let a = Some(1u8).map(|a| foo(a));
    = note: `-D clippy::redundant-closure` implied by `-D warnings`
 
 error: redundant closure
-  --> $DIR/eta.rs:33:10
+  --> $DIR/eta.rs:35:40
    |
-LL |     meta(|a| foo(a));
-   |          ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
+LL |     let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
+   |                                        ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
 
 error: redundant closure
-  --> $DIR/eta.rs:37:40
+  --> $DIR/eta.rs:36:35
    |
-LL |     let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
-   |                                        ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
+LL |     let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
+   |                                   ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
 
 error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler
-  --> $DIR/eta.rs:39:21
+  --> $DIR/eta.rs:37:21
    |
 LL |     all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
    |                     ^^^ help: change this to: `&2`
@@ -27,13 +27,25 @@ LL |     all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
    = note: `-D clippy::needless-borrow` implied by `-D warnings`
 
 error: redundant closure
-  --> $DIR/eta.rs:46:27
+  --> $DIR/eta.rs:37:26
+   |
+LL |     all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
+   |                          ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
+
+error: redundant closure
+  --> $DIR/eta.rs:43:27
+   |
+LL |     let e = Some(1u8).map(|a| divergent(a));
+   |                           ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent`
+
+error: redundant closure
+  --> $DIR/eta.rs:44:27
    |
 LL |     let e = Some(1u8).map(|a| generic(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
 
 error: redundant closure
-  --> $DIR/eta.rs:92:51
+  --> $DIR/eta.rs:90:51
    |
 LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
    |                                                   ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
@@ -41,70 +53,82 @@ LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
    = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
 
 error: redundant closure
-  --> $DIR/eta.rs:94:51
+  --> $DIR/eta.rs:91:51
    |
 LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
    |                                                   ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
 
 error: redundant closure
-  --> $DIR/eta.rs:97:42
+  --> $DIR/eta.rs:93:42
    |
 LL |     let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
    |                                          ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
 
 error: redundant closure
-  --> $DIR/eta.rs:102:29
+  --> $DIR/eta.rs:97:29
    |
 LL |     let e = Some("str").map(|s| s.to_string());
    |                             ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
 
 error: redundant closure
-  --> $DIR/eta.rs:104:27
+  --> $DIR/eta.rs:98:27
    |
 LL |     let e = Some('a').map(|s| s.to_uppercase());
    |                           ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
 
 error: redundant closure
-  --> $DIR/eta.rs:107:65
+  --> $DIR/eta.rs:100:65
    |
 LL |     let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
 
 error: redundant closure
-  --> $DIR/eta.rs:190:27
+  --> $DIR/eta.rs:163:22
+   |
+LL |     requires_fn_once(|| x());
+   |                      ^^^^^^ help: replace the closure with the function itself: `x`
+
+error: redundant closure
+  --> $DIR/eta.rs:170:27
    |
 LL |     let a = Some(1u8).map(|a| foo_ptr(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
 
 error: redundant closure
-  --> $DIR/eta.rs:195:27
+  --> $DIR/eta.rs:175:27
    |
 LL |     let a = Some(1u8).map(|a| closure(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
 
 error: redundant closure
-  --> $DIR/eta.rs:227:28
+  --> $DIR/eta.rs:207:28
    |
 LL |     x.into_iter().for_each(|x| add_to_res(x));
    |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
 
 error: redundant closure
-  --> $DIR/eta.rs:228:28
+  --> $DIR/eta.rs:208:28
    |
 LL |     y.into_iter().for_each(|x| add_to_res(x));
    |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
 
 error: redundant closure
-  --> $DIR/eta.rs:229:28
+  --> $DIR/eta.rs:209:28
    |
 LL |     z.into_iter().for_each(|x| add_to_res(x));
    |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
 
 error: redundant closure
-  --> $DIR/eta.rs:236:21
+  --> $DIR/eta.rs:216:21
    |
 LL |         Some(1).map(|n| closure(n));
    |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
 
-error: aborting due to 17 previous errors
+error: redundant closure
+  --> $DIR/eta.rs:235:21
+   |
+LL |     map_str_to_path(|s| s.as_ref());
+   |                     ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref`
+
+error: aborting due to 21 previous errors
 
index d742856bc4163af0999ad145af09e9a6d6dc942d..8e6a32b7be33d2eccde04199653c2d467942f3c0 100644 (file)
@@ -4,7 +4,6 @@
 #[allow(
     unused_assignments,
     unused_variables,
-    clippy::many_single_char_names,
     clippy::no_effect,
     dead_code,
     clippy::blacklisted_name
index 35eb85e95a320bf72497178054bf1a1a312bc20c..4f611e308e18619e50f0b408ed7c07f60c28b47d 100644 (file)
@@ -1,48 +1,48 @@
 error: unsequenced read of `x`
-  --> $DIR/eval_order_dependence.rs:17:9
+  --> $DIR/eval_order_dependence.rs:16:9
    |
 LL |     } + x;
    |         ^
    |
    = note: `-D clippy::eval-order-dependence` implied by `-D warnings`
 note: whether read occurs before this write depends on evaluation order
-  --> $DIR/eval_order_dependence.rs:15:9
+  --> $DIR/eval_order_dependence.rs:14:9
    |
 LL |         x = 1;
    |         ^^^^^
 
 error: unsequenced read of `x`
-  --> $DIR/eval_order_dependence.rs:20:5
+  --> $DIR/eval_order_dependence.rs:19:5
    |
 LL |     x += {
    |     ^
    |
 note: whether read occurs before this write depends on evaluation order
-  --> $DIR/eval_order_dependence.rs:21:9
+  --> $DIR/eval_order_dependence.rs:20:9
    |
 LL |         x = 20;
    |         ^^^^^^
 
 error: unsequenced read of `x`
-  --> $DIR/eval_order_dependence.rs:33:12
+  --> $DIR/eval_order_dependence.rs:32:12
    |
 LL |         a: x,
    |            ^
    |
 note: whether read occurs before this write depends on evaluation order
-  --> $DIR/eval_order_dependence.rs:35:13
+  --> $DIR/eval_order_dependence.rs:34:13
    |
 LL |             x = 6;
    |             ^^^^^
 
 error: unsequenced read of `x`
-  --> $DIR/eval_order_dependence.rs:42:9
+  --> $DIR/eval_order_dependence.rs:41:9
    |
 LL |         x += {
    |         ^
    |
 note: whether read occurs before this write depends on evaluation order
-  --> $DIR/eval_order_dependence.rs:43:13
+  --> $DIR/eval_order_dependence.rs:42:13
    |
 LL |             x = 20;
    |             ^^^^^^
index bf0325fec7923f9cf40b43b4ad2333e873891d94..90376620a9fd835d867fbe9b7bdc8538321628cc 100644 (file)
@@ -17,8 +17,8 @@ fn main() {
     const BAD32_3: f32 = 0.1;
     const BAD32_EDGE: f32 = 1.000_001;
 
-    const BAD64_1: f64 = 0.123_456_789_012_345_66_f64;
-    const BAD64_2: f64 = 0.123_456_789_012_345_66;
+    const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
+    const BAD64_2: f64 = 0.123_456_789_012_345_67;
     const BAD64_3: f64 = 0.1;
 
     // Literal as param
@@ -37,9 +37,9 @@ fn main() {
     let bad32_suf: f32 = 1.123_456_8_f32;
     let bad32_inf = 1.123_456_8_f32;
 
-    let bad64: f64 = 0.123_456_789_012_345_66;
-    let bad64_suf: f64 = 0.123_456_789_012_345_66_f64;
-    let bad64_inf = 0.123_456_789_012_345_66;
+    let bad64: f64 = 0.123_456_789_012_345_67;
+    let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
+    let bad64_inf = 0.123_456_789_012_345_67;
 
     // Vectors
     let good_vec32: Vec<f32> = vec![0.123_456];
index 599773f2f70c4378984b5c1720d56b36768cb264..e59c20c30b4fda7bbfcceb15b60f2ceff69d9cb4 100644 (file)
@@ -24,18 +24,6 @@ error: float has excessive precision
 LL |     const BAD32_EDGE: f32 = 1.000_000_9;
    |                             ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001`
 
-error: float has excessive precision
-  --> $DIR/excessive_precision.rs:20:26
-   |
-LL |     const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
-   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
-
-error: float has excessive precision
-  --> $DIR/excessive_precision.rs:21:26
-   |
-LL |     const BAD64_2: f64 = 0.123_456_789_012_345_67;
-   |                          ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
-
 error: float has excessive precision
   --> $DIR/excessive_precision.rs:22:26
    |
@@ -66,24 +54,6 @@ error: float has excessive precision
 LL |     let bad32_inf = 1.123_456_789_f32;
    |                     ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
 
-error: float has excessive precision
-  --> $DIR/excessive_precision.rs:40:22
-   |
-LL |     let bad64: f64 = 0.123_456_789_012_345_67;
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
-
-error: float has excessive precision
-  --> $DIR/excessive_precision.rs:41:26
-   |
-LL |     let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
-   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
-
-error: float has excessive precision
-  --> $DIR/excessive_precision.rs:42:21
-   |
-LL |     let bad64_inf = 0.123_456_789_012_345_67;
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
-
 error: float has excessive precision
   --> $DIR/excessive_precision.rs:48:36
    |
@@ -108,5 +78,5 @@ error: float has excessive precision
 LL |     let bad_bige32: f32 = 1.123_456_788_888E-10;
    |                           ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10`
 
-error: aborting due to 18 previous errors
+error: aborting due to 13 previous errors
 
index 51d0468e47ca41392103bb164d31ebf005e04154..48e2aae75d0bf62893c56b4a375d7f9054e5f6c9 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
+#![allow(unused_variables, clippy::clone_double_ref)]
 #![warn(clippy::explicit_deref_methods)]
 
 use std::ops::{Deref, DerefMut};
index 680664bd4f6f26ac3facdd7efa2f10cd5e12c948..d8c8c0c5ca329c52d703180117d472391d1888b0 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 
-#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
+#![allow(unused_variables, clippy::clone_double_ref)]
 #![warn(clippy::explicit_deref_methods)]
 
 use std::ops::{Deref, DerefMut};
index 5d5af4e4632970afe821f9b42b82eae5bd445e08..495cd97e05e15d472bef961da5a2e9eab1573814 100644 (file)
@@ -1,4 +1,5 @@
 #![deny(clippy::fallible_impl_from)]
+#![allow(clippy::if_then_panic)]
 
 // docs example
 struct Foo(i32);
index 64c8ea857277e2af5b4a81e4390d55c2c7be23a3..8b8054586e690ecef0fd4f0b794494d1005616b7 100644 (file)
@@ -1,5 +1,5 @@
 error: consider implementing `TryFrom` instead
-  --> $DIR/fallible_impl_from.rs:5:1
+  --> $DIR/fallible_impl_from.rs:6:1
    |
 LL | / impl From<String> for Foo {
 LL | |     fn from(s: String) -> Self {
@@ -15,13 +15,13 @@ LL | #![deny(clippy::fallible_impl_from)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
 note: potential failure(s)
-  --> $DIR/fallible_impl_from.rs:7:13
+  --> $DIR/fallible_impl_from.rs:8:13
    |
 LL |         Foo(s.parse().unwrap())
    |             ^^^^^^^^^^^^^^^^^^
 
 error: consider implementing `TryFrom` instead
-  --> $DIR/fallible_impl_from.rs:26:1
+  --> $DIR/fallible_impl_from.rs:27:1
    |
 LL | / impl From<usize> for Invalid {
 LL | |     fn from(i: usize) -> Invalid {
@@ -34,14 +34,14 @@ LL | | }
    |
    = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
 note: potential failure(s)
-  --> $DIR/fallible_impl_from.rs:29:13
+  --> $DIR/fallible_impl_from.rs:30:13
    |
 LL |             panic!();
    |             ^^^^^^^^^
    = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: consider implementing `TryFrom` instead
-  --> $DIR/fallible_impl_from.rs:35:1
+  --> $DIR/fallible_impl_from.rs:36:1
    |
 LL | / impl From<Option<String>> for Invalid {
 LL | |     fn from(s: Option<String>) -> Invalid {
@@ -54,7 +54,7 @@ LL | | }
    |
    = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
 note: potential failure(s)
-  --> $DIR/fallible_impl_from.rs:37:17
+  --> $DIR/fallible_impl_from.rs:38:17
    |
 LL |         let s = s.unwrap();
    |                 ^^^^^^^^^^
@@ -68,7 +68,7 @@ LL |             panic!("{:?}", s);
    = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: consider implementing `TryFrom` instead
-  --> $DIR/fallible_impl_from.rs:53:1
+  --> $DIR/fallible_impl_from.rs:54:1
    |
 LL | / impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
 LL | |     fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
@@ -81,7 +81,7 @@ LL | | }
    |
    = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
 note: potential failure(s)
-  --> $DIR/fallible_impl_from.rs:55:12
+  --> $DIR/fallible_impl_from.rs:56:12
    |
 LL |         if s.parse::<u32>().ok().unwrap() != 42 {
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index ad5d1a09c0345b54e36642758d86e70d409e1695..a34458b9419516964d651f7fb281bec654d01604 100644 (file)
@@ -4,8 +4,7 @@
     clippy::no_effect,
     clippy::op_ref,
     clippy::unnecessary_operation,
-    clippy::cast_lossless,
-    clippy::many_single_char_names
+    clippy::cast_lossless
 )]
 
 use std::ops::Add;
index cb5b68b2e958500d00ffe8a2c7d9f3a61fc54d9d..9cc1f1b75ed4492d679c55b200117670e7a61f9d 100644 (file)
@@ -1,5 +1,5 @@
 error: strict comparison of `f32` or `f64`
-  --> $DIR/float_cmp.rs:58:5
+  --> $DIR/float_cmp.rs:57:5
    |
 LL |     ONE as f64 != 2.0;
    |     ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin`
@@ -8,7 +8,7 @@ LL |     ONE as f64 != 2.0;
    = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
-  --> $DIR/float_cmp.rs:63:5
+  --> $DIR/float_cmp.rs:62:5
    |
 LL |     x == 1.0;
    |     ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin`
@@ -16,7 +16,7 @@ LL |     x == 1.0;
    = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
-  --> $DIR/float_cmp.rs:66:5
+  --> $DIR/float_cmp.rs:65:5
    |
 LL |     twice(x) != twice(ONE as f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin`
@@ -24,7 +24,7 @@ LL |     twice(x) != twice(ONE as f64);
    = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
-  --> $DIR/float_cmp.rs:86:5
+  --> $DIR/float_cmp.rs:85:5
    |
 LL |     NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin`
@@ -32,7 +32,7 @@ LL |     NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
    = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` arrays
-  --> $DIR/float_cmp.rs:91:5
+  --> $DIR/float_cmp.rs:90:5
    |
 LL |     a1 == a2;
    |     ^^^^^^^^
@@ -40,7 +40,7 @@ LL |     a1 == a2;
    = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
-  --> $DIR/float_cmp.rs:92:5
+  --> $DIR/float_cmp.rs:91:5
    |
 LL |     a1[0] == a2[0];
    |     ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin`
index f44928d4083868968a448727c64cce4ba20ecb8a..f0e4835415f306411b1a6d97049e9abc361194af 100644 (file)
@@ -29,7 +29,7 @@ impl Unrelated {
     clippy::unnecessary_mut_passed,
     clippy::similar_names
 )]
-#[allow(clippy::many_single_char_names, unused_variables)]
+#[allow(unused_variables)]
 fn main() {
     let mut vec = vec![1, 2, 3, 4];
 
index 5b1eb3ee4dcd2421440fe4332b17c8a00af23c0f..1edef175fb9833e297a91c5c7d550e2884e57e49 100644 (file)
@@ -29,7 +29,7 @@ fn iter(&self) -> std::slice::Iter<u8> {
     clippy::unnecessary_mut_passed,
     clippy::similar_names
 )]
-#[allow(clippy::many_single_char_names, unused_variables)]
+#[allow(unused_variables)]
 fn main() {
     let mut vec = vec![1, 2, 3, 4];
 
diff --git a/src/tools/clippy/tests/ui/if_let_some_result.fixed b/src/tools/clippy/tests/ui/if_let_some_result.fixed
deleted file mode 100644 (file)
index 1bddc47..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-// run-rustfix
-
-#![warn(clippy::if_let_some_result)]
-#![allow(dead_code)]
-
-fn str_to_int(x: &str) -> i32 {
-    if let Ok(y) = x.parse() { y } else { 0 }
-}
-
-fn str_to_int_ok(x: &str) -> i32 {
-    if let Ok(y) = x.parse() { y } else { 0 }
-}
-
-#[rustfmt::skip]
-fn strange_some_no_else(x: &str) -> i32 {
-    {
-        if let Ok(y) = x   .   parse()       {
-            return y;
-        };
-        0
-    }
-}
-
-fn negative() {
-    while let Some(1) = "".parse().ok() {}
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/if_let_some_result.rs b/src/tools/clippy/tests/ui/if_let_some_result.rs
deleted file mode 100644 (file)
index d4a52ec..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-// run-rustfix
-
-#![warn(clippy::if_let_some_result)]
-#![allow(dead_code)]
-
-fn str_to_int(x: &str) -> i32 {
-    if let Some(y) = x.parse().ok() { y } else { 0 }
-}
-
-fn str_to_int_ok(x: &str) -> i32 {
-    if let Ok(y) = x.parse() { y } else { 0 }
-}
-
-#[rustfmt::skip]
-fn strange_some_no_else(x: &str) -> i32 {
-    {
-        if let Some(y) = x   .   parse()   .   ok   ()    {
-            return y;
-        };
-        0
-    }
-}
-
-fn negative() {
-    while let Some(1) = "".parse().ok() {}
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/if_let_some_result.stderr b/src/tools/clippy/tests/ui/if_let_some_result.stderr
deleted file mode 100644 (file)
index bc3a5e7..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-error: matching on `Some` with `ok()` is redundant
-  --> $DIR/if_let_some_result.rs:7:5
-   |
-LL |     if let Some(y) = x.parse().ok() { y } else { 0 }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::if-let-some-result` implied by `-D warnings`
-help: consider matching on `Ok(y)` and removing the call to `ok` instead
-   |
-LL |     if let Ok(y) = x.parse() { y } else { 0 }
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~
-
-error: matching on `Some` with `ok()` is redundant
-  --> $DIR/if_let_some_result.rs:17:9
-   |
-LL |         if let Some(y) = x   .   parse()   .   ok   ()    {
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: consider matching on `Ok(y)` and removing the call to `ok` instead
-   |
-LL |         if let Ok(y) = x   .   parse()       {
-   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error: aborting due to 2 previous errors
-
diff --git a/src/tools/clippy/tests/ui/if_then_panic.fixed b/src/tools/clippy/tests/ui/if_then_panic.fixed
new file mode 100644 (file)
index 0000000..fc57ae0
--- /dev/null
@@ -0,0 +1,34 @@
+// run-rustfix
+#![warn(clippy::if_then_panic)]
+
+fn main() {
+    let a = vec![1, 2, 3];
+    let c = Some(2);
+    if !a.is_empty()
+        && a.len() == 3
+        && c != None
+        && !a.is_empty()
+        && a.len() == 3
+        && !a.is_empty()
+        && a.len() == 3
+        && !a.is_empty()
+        && a.len() == 3
+    {
+        panic!("qaqaq{:?}", a);
+    }
+    assert!(a.is_empty(), "qaqaq{:?}", a);
+    assert!(a.is_empty(), "qwqwq");
+    if a.len() == 3 {
+        println!("qwq");
+        println!("qwq");
+        println!("qwq");
+    }
+    if let Some(b) = c {
+        panic!("orz {}", b);
+    }
+    if a.len() == 3 {
+        panic!("qaqaq");
+    } else {
+        println!("qwq");
+    }
+}
diff --git a/src/tools/clippy/tests/ui/if_then_panic.rs b/src/tools/clippy/tests/ui/if_then_panic.rs
new file mode 100644 (file)
index 0000000..d1ac93d
--- /dev/null
@@ -0,0 +1,38 @@
+// run-rustfix
+#![warn(clippy::if_then_panic)]
+
+fn main() {
+    let a = vec![1, 2, 3];
+    let c = Some(2);
+    if !a.is_empty()
+        && a.len() == 3
+        && c != None
+        && !a.is_empty()
+        && a.len() == 3
+        && !a.is_empty()
+        && a.len() == 3
+        && !a.is_empty()
+        && a.len() == 3
+    {
+        panic!("qaqaq{:?}", a);
+    }
+    if !a.is_empty() {
+        panic!("qaqaq{:?}", a);
+    }
+    if !a.is_empty() {
+        panic!("qwqwq");
+    }
+    if a.len() == 3 {
+        println!("qwq");
+        println!("qwq");
+        println!("qwq");
+    }
+    if let Some(b) = c {
+        panic!("orz {}", b);
+    }
+    if a.len() == 3 {
+        panic!("qaqaq");
+    } else {
+        println!("qwq");
+    }
+}
diff --git a/src/tools/clippy/tests/ui/if_then_panic.stderr b/src/tools/clippy/tests/ui/if_then_panic.stderr
new file mode 100644 (file)
index 0000000..b92c9bd
--- /dev/null
@@ -0,0 +1,20 @@
+error: only a `panic!` in `if`-then statement
+  --> $DIR/if_then_panic.rs:19:5
+   |
+LL | /     if !a.is_empty() {
+LL | |         panic!("qaqaq{:?}", a);
+LL | |     }
+   | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);`
+   |
+   = note: `-D clippy::if-then-panic` implied by `-D warnings`
+
+error: only a `panic!` in `if`-then statement
+  --> $DIR/if_then_panic.rs:22:5
+   |
+LL | /     if !a.is_empty() {
+LL | |         panic!("qwqwq");
+LL | |     }
+   | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
+
+error: aborting due to 2 previous errors
+
index e518b2677b7bf351d73e6941e236641433177d0a..e86bd7bcf4faf0e5ed40c749fd0383b60fba22bc 100644 (file)
@@ -16,7 +16,6 @@ fn foob() -> bool {
     unimplemented!()
 }
 
-#[allow(clippy::many_single_char_names)]
 fn immutable_condition() {
     // Should warn when all vars mentioned are immutable
     let y = 0;
index 2736753c14b6eccfc33acd4575f63efca21f89e5..69309b0da877cdbbd6c6ae2cf85749f56e2fdfb0 100644 (file)
@@ -1,5 +1,5 @@
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:23:11
+  --> $DIR/infinite_loop.rs:22:11
    |
 LL |     while y < 10 {
    |           ^^^^^^
@@ -8,7 +8,7 @@ LL |     while y < 10 {
    = note: this may lead to an infinite or to a never running loop
 
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:28:11
+  --> $DIR/infinite_loop.rs:27:11
    |
 LL |     while y < 10 && x < 3 {
    |           ^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     while y < 10 && x < 3 {
    = note: this may lead to an infinite or to a never running loop
 
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:35:11
+  --> $DIR/infinite_loop.rs:34:11
    |
 LL |     while !cond {
    |           ^^^^^
@@ -24,7 +24,7 @@ LL |     while !cond {
    = note: this may lead to an infinite or to a never running loop
 
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:79:11
+  --> $DIR/infinite_loop.rs:78:11
    |
 LL |     while i < 3 {
    |           ^^^^^
@@ -32,7 +32,7 @@ LL |     while i < 3 {
    = note: this may lead to an infinite or to a never running loop
 
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:84:11
+  --> $DIR/infinite_loop.rs:83:11
    |
 LL |     while i < 3 && j > 0 {
    |           ^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     while i < 3 && j > 0 {
    = note: this may lead to an infinite or to a never running loop
 
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:88:11
+  --> $DIR/infinite_loop.rs:87:11
    |
 LL |     while i < 3 {
    |           ^^^^^
@@ -48,7 +48,7 @@ LL |     while i < 3 {
    = note: this may lead to an infinite or to a never running loop
 
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:103:11
+  --> $DIR/infinite_loop.rs:102:11
    |
 LL |     while i < 3 {
    |           ^^^^^
@@ -56,7 +56,7 @@ LL |     while i < 3 {
    = note: this may lead to an infinite or to a never running loop
 
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:108:11
+  --> $DIR/infinite_loop.rs:107:11
    |
 LL |     while i < 3 {
    |           ^^^^^
@@ -64,7 +64,7 @@ LL |     while i < 3 {
    = note: this may lead to an infinite or to a never running loop
 
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:174:15
+  --> $DIR/infinite_loop.rs:173:15
    |
 LL |         while self.count < n {
    |               ^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL |         while self.count < n {
    = note: this may lead to an infinite or to a never running loop
 
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:182:11
+  --> $DIR/infinite_loop.rs:181:11
    |
 LL |     while y < 10 {
    |           ^^^^^^
@@ -82,7 +82,7 @@ LL |     while y < 10 {
    = help: rewrite it as `if cond { loop { } }`
 
 error: variables in the condition are not mutated in the loop body
-  --> $DIR/infinite_loop.rs:189:11
+  --> $DIR/infinite_loop.rs:188:11
    |
 LL |     while y < 10 {
    |           ^^^^^^
index 6e65fdbd04e7dafc7acfae04f81d757218b2bb9c..aeb0a0c1e2e841814d5168ee241db632caae43ef 100644 (file)
@@ -1,6 +1,5 @@
 #![warn(clippy::inherent_to_string)]
 #![deny(clippy::inherent_to_string_shadow_display)]
-#![allow(clippy::many_single_char_names)]
 
 use std::fmt;
 
index f5fcc193b4d8a2332af013451b679751b0629d68..4f331f5bec9e6fe87073d1cdb63991862db4bf28 100644 (file)
@@ -1,5 +1,5 @@
 error: implementation of inherent method `to_string(&self) -> String` for type `A`
-  --> $DIR/inherent_to_string.rs:21:5
+  --> $DIR/inherent_to_string.rs:20:5
    |
 LL | /     fn to_string(&self) -> String {
 LL | |         "A.to_string()".to_string()
@@ -10,7 +10,7 @@ LL | |     }
    = help: implement trait `Display` for type `A` instead
 
 error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`
-  --> $DIR/inherent_to_string.rs:45:5
+  --> $DIR/inherent_to_string.rs:44:5
    |
 LL | /     fn to_string(&self) -> String {
 LL | |         "C.to_string()".to_string()
diff --git a/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs b/src/tools/clippy/tests/ui/iter_not_returning_iterator.rs
new file mode 100644 (file)
index 0000000..377f760
--- /dev/null
@@ -0,0 +1,47 @@
+#![warn(clippy::iter_not_returning_iterator)]
+
+struct Data {
+    begin: u32,
+}
+
+struct Counter {
+    count: u32,
+}
+
+impl Data {
+    fn iter(&self) -> Counter {
+        todo!()
+    }
+
+    fn iter_mut(&self) -> Counter {
+        todo!()
+    }
+}
+
+struct Data2 {
+    begin: u32,
+}
+
+struct Counter2 {
+    count: u32,
+}
+
+impl Data2 {
+    fn iter(&self) -> Counter2 {
+        todo!()
+    }
+
+    fn iter_mut(&self) -> Counter2 {
+        todo!()
+    }
+}
+
+impl Iterator for Counter {
+    type Item = u32;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        todo!()
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr b/src/tools/clippy/tests/ui/iter_not_returning_iterator.stderr
new file mode 100644 (file)
index 0000000..2273cd0
--- /dev/null
@@ -0,0 +1,16 @@
+error: this method is named `iter` but its return type does not implement `Iterator`
+  --> $DIR/iter_not_returning_iterator.rs:30:5
+   |
+LL |     fn iter(&self) -> Counter2 {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::iter-not-returning-iterator` implied by `-D warnings`
+
+error: this method is named `iter_mut` but its return type does not implement `Iterator`
+  --> $DIR/iter_not_returning_iterator.rs:34:5
+   |
+LL |     fn iter_mut(&self) -> Counter2 {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
index a01c6ef99db921386fb8ea8bdaac007cb804c08a..4eaa2dd98eb2e386ae21e4bed3695a74a63b8896 100644 (file)
@@ -1,4 +1,4 @@
-#![allow(unused, clippy::many_single_char_names, clippy::diverging_sub_expression)]
+#![allow(unused, clippy::diverging_sub_expression)]
 #![warn(clippy::logic_bug)]
 
 fn main() {
index 3a0332939d409a8237572e97d489bce948b44817..992baf1f185a710ae1d7d3ba70207821ee9044b0 100644 (file)
@@ -36,6 +36,12 @@ fn main() {
 
     // Don't lint, slices don't have `split_once`
     let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
+
+    // `rsplitn` gives the results in the reverse order of `rsplit_once`
+    let _ = "key=value".rsplit_once('=').unwrap().1;
+    let _ = "key=value".rsplit_once('=').map_or("key=value", |x| x.0);
+    let _ = "key=value".rsplit_once('=').map(|x| x.1);
+    let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
 }
 
 fn _msrv_1_51() {
index e6093b63fe8d428e348760d6a496a3ae4d433090..4f92ab6b812bd87fe642d43e2fceff2d963e8116 100644 (file)
@@ -36,6 +36,12 @@ fn _f(s: &str) -> Option<&str> {
 
     // Don't lint, slices don't have `split_once`
     let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
+
+    // `rsplitn` gives the results in the reverse order of `rsplit_once`
+    let _ = "key=value".rsplitn(2, '=').next().unwrap();
+    let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
+    let _ = "key=value".rsplitn(2, '=').nth(0);
+    let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
 }
 
 fn _msrv_1_51() {
index 4f15196b469e1f73d713f527edca2c57b22af889..7bea2303d9213ab9d49c0d7cae676ce730738f60 100644 (file)
@@ -72,11 +72,35 @@ error: manual implementation of `split_once`
 LL |         let _ = s.splitn(2, "key=value").skip(1).next()?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
 
+error: manual implementation of `rsplit_once`
+  --> $DIR/manual_split_once.rs:41:13
+   |
+LL |     let _ = "key=value".rsplitn(2, '=').next().unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().1`
+
+error: manual implementation of `rsplit_once`
+  --> $DIR/manual_split_once.rs:42:13
+   |
+LL |     let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map_or("key=value", |x| x.0)`
+
+error: manual implementation of `rsplit_once`
+  --> $DIR/manual_split_once.rs:43:13
+   |
+LL |     let _ = "key=value".rsplitn(2, '=').nth(0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)`
+
+error: manual implementation of `rsplit_once`
+  --> $DIR/manual_split_once.rs:44:18
+   |
+LL |     let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
+
 error: manual implementation of `split_once`
-  --> $DIR/manual_split_once.rs:49:13
+  --> $DIR/manual_split_once.rs:55:13
    |
 LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 
-error: aborting due to 13 previous errors
+error: aborting due to 17 previous errors
 
index 80800e487248fe52042fb9f15016a0f0a085098e..65769819110eb6f00b05332c7ae0b54c6ba8fa2b 100644 (file)
@@ -1,4 +1,4 @@
-#[warn(clippy::many_single_char_names)]
+#![warn(clippy::many_single_char_names)]
 
 fn bla() {
     let a: i32;
index 18846c898da051566ee99796f98af0724f91def5..fec3a95edd62da54cddde339c8566e0fa56656e9 100644 (file)
@@ -4,6 +4,7 @@
 #![allow(clippy::let_underscore_drop)]
 #![allow(clippy::missing_docs_in_private_items)]
 #![allow(clippy::map_identity)]
+#![allow(clippy::redundant_closure)]
 #![allow(clippy::unnecessary_wraps)]
 #![feature(result_flattening)]
 
index 01db27876da703a698c4b0da0ccef2dcba2cf790..aa1f76e335af0dad3a395921b3e25e6fbf82b6a4 100644 (file)
@@ -4,6 +4,7 @@
 #![allow(clippy::let_underscore_drop)]
 #![allow(clippy::missing_docs_in_private_items)]
 #![allow(clippy::map_identity)]
+#![allow(clippy::redundant_closure)]
 #![allow(clippy::unnecessary_wraps)]
 #![feature(result_flattening)]
 
index 38457c8ea4dd4ad16de7cb16eb7c880c90d176cf..bcd2047e6faa3d3683b11f396e6a818855c94d21 100644 (file)
@@ -1,5 +1,5 @@
 error: called `map(..).flatten()` on an `Iterator`
-  --> $DIR/map_flatten.rs:17:46
+  --> $DIR/map_flatten.rs:18:46
    |
 LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
    |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)`
@@ -7,37 +7,37 @@ LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll
    = note: `-D clippy::map-flatten` implied by `-D warnings`
 
 error: called `map(..).flatten()` on an `Iterator`
-  --> $DIR/map_flatten.rs:18:46
+  --> $DIR/map_flatten.rs:19:46
    |
 LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
    |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)`
 
 error: called `map(..).flatten()` on an `Iterator`
-  --> $DIR/map_flatten.rs:19:46
+  --> $DIR/map_flatten.rs:20:46
    |
 LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
    |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)`
 
 error: called `map(..).flatten()` on an `Iterator`
-  --> $DIR/map_flatten.rs:20:46
+  --> $DIR/map_flatten.rs:21:46
    |
 LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
    |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))`
 
 error: called `map(..).flatten()` on an `Iterator`
-  --> $DIR/map_flatten.rs:23:46
+  --> $DIR/map_flatten.rs:24:46
    |
 LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
    |                                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)`
 
 error: called `map(..).flatten()` on an `Option`
-  --> $DIR/map_flatten.rs:26:39
+  --> $DIR/map_flatten.rs:27:39
    |
 LL |     let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
    |                                       ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
 
 error: called `map(..).flatten()` on an `Result`
-  --> $DIR/map_flatten.rs:29:41
+  --> $DIR/map_flatten.rs:30:41
    |
 LL |     let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
    |                                         ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
diff --git a/src/tools/clippy/tests/ui/match_result_ok.fixed b/src/tools/clippy/tests/ui/match_result_ok.fixed
new file mode 100644 (file)
index 0000000..d4760a9
--- /dev/null
@@ -0,0 +1,63 @@
+// run-rustfix
+
+#![warn(clippy::match_result_ok)]
+#![allow(clippy::boxed_local)]
+#![allow(dead_code)]
+
+// Checking `if` cases
+
+fn str_to_int(x: &str) -> i32 {
+    if let Ok(y) = x.parse() { y } else { 0 }
+}
+
+fn str_to_int_ok(x: &str) -> i32 {
+    if let Ok(y) = x.parse() { y } else { 0 }
+}
+
+#[rustfmt::skip]
+fn strange_some_no_else(x: &str) -> i32 {
+    {
+        if let Ok(y) = x   .   parse()       {
+            return y;
+        };
+        0
+    }
+}
+
+// Checking `while` cases
+
+struct Wat {
+    counter: i32,
+}
+
+impl Wat {
+    fn next(&mut self) -> Result<i32, &str> {
+        self.counter += 1;
+        if self.counter < 5 {
+            Ok(self.counter)
+        } else {
+            Err("Oh no")
+        }
+    }
+}
+
+fn base_1(x: i32) {
+    let mut wat = Wat { counter: x };
+    while let Ok(a) = wat.next() {
+        println!("{}", a);
+    }
+}
+
+fn base_2(x: i32) {
+    let mut wat = Wat { counter: x };
+    while let Ok(a) = wat.next() {
+        println!("{}", a);
+    }
+}
+
+fn base_3(test_func: Box<Result<i32, &str>>) {
+    // Expected to stay as is
+    while let Some(_b) = test_func.ok() {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_result_ok.rs b/src/tools/clippy/tests/ui/match_result_ok.rs
new file mode 100644 (file)
index 0000000..0b81872
--- /dev/null
@@ -0,0 +1,63 @@
+// run-rustfix
+
+#![warn(clippy::match_result_ok)]
+#![allow(clippy::boxed_local)]
+#![allow(dead_code)]
+
+// Checking `if` cases
+
+fn str_to_int(x: &str) -> i32 {
+    if let Some(y) = x.parse().ok() { y } else { 0 }
+}
+
+fn str_to_int_ok(x: &str) -> i32 {
+    if let Ok(y) = x.parse() { y } else { 0 }
+}
+
+#[rustfmt::skip]
+fn strange_some_no_else(x: &str) -> i32 {
+    {
+        if let Some(y) = x   .   parse()   .   ok   ()    {
+            return y;
+        };
+        0
+    }
+}
+
+// Checking `while` cases
+
+struct Wat {
+    counter: i32,
+}
+
+impl Wat {
+    fn next(&mut self) -> Result<i32, &str> {
+        self.counter += 1;
+        if self.counter < 5 {
+            Ok(self.counter)
+        } else {
+            Err("Oh no")
+        }
+    }
+}
+
+fn base_1(x: i32) {
+    let mut wat = Wat { counter: x };
+    while let Some(a) = wat.next().ok() {
+        println!("{}", a);
+    }
+}
+
+fn base_2(x: i32) {
+    let mut wat = Wat { counter: x };
+    while let Ok(a) = wat.next() {
+        println!("{}", a);
+    }
+}
+
+fn base_3(test_func: Box<Result<i32, &str>>) {
+    // Expected to stay as is
+    while let Some(_b) = test_func.ok() {}
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_result_ok.stderr b/src/tools/clippy/tests/ui/match_result_ok.stderr
new file mode 100644 (file)
index 0000000..cc3bc8c
--- /dev/null
@@ -0,0 +1,36 @@
+error: matching on `Some` with `ok()` is redundant
+  --> $DIR/match_result_ok.rs:10:5
+   |
+LL |     if let Some(y) = x.parse().ok() { y } else { 0 }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::match-result-ok` implied by `-D warnings`
+help: consider matching on `Ok(y)` and removing the call to `ok` instead
+   |
+LL |     if let Ok(y) = x.parse() { y } else { 0 }
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: matching on `Some` with `ok()` is redundant
+  --> $DIR/match_result_ok.rs:20:9
+   |
+LL |         if let Some(y) = x   .   parse()   .   ok   ()    {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider matching on `Ok(y)` and removing the call to `ok` instead
+   |
+LL |         if let Ok(y) = x   .   parse()       {
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: matching on `Some` with `ok()` is redundant
+  --> $DIR/match_result_ok.rs:46:5
+   |
+LL |     while let Some(a) = wat.next().ok() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider matching on `Ok(a)` and removing the call to `ok` instead
+   |
+LL |     while let Ok(a) = wat.next() {
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 3 previous errors
+
index 30bf64022533c7bbc452e04f3176756e74718a5e..b4ec525ada09a763059d8837fc06d572051f1a4b 100644 (file)
@@ -1,7 +1,7 @@
 // run-rustfix
 
 #![warn(clippy::match_single_binding)]
-#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
+#![allow(unused_variables, clippy::toplevel_ref_arg)]
 
 struct Point {
     x: i32,
index d8bb80d8b96c49fbe58722f9312e88f10f3e286b..e04c4018b98ddbb52e282768d113bb7e25ab4f63 100644 (file)
@@ -1,7 +1,7 @@
 // run-rustfix
 
 #![warn(clippy::match_single_binding)]
-#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
+#![allow(unused_variables, clippy::toplevel_ref_arg)]
 
 struct Point {
     x: i32,
index 2d227e6654c36c0f45e6801205dd173f271d546a..1c0ba664580a40d253923a2ec937db47efac8049 100644 (file)
@@ -1,6 +1,9 @@
-use std::collections::{HashMap, HashSet};
+use std::cell::Cell;
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
 use std::hash::{Hash, Hasher};
+use std::rc::Rc;
 use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+use std::sync::Arc;
 
 struct Key(AtomicUsize);
 
@@ -31,11 +34,19 @@ fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<K
 
 fn this_is_ok(_m: &mut HashMap<usize, Key>) {}
 
+// Raw pointers are hashed by the address they point to, so it doesn't matter if they point to a
+// type with interior mutability.  See:
+// - clippy issue: https://github.com/rust-lang/rust-clippy/issues/6745
+// - std lib: https://github.com/rust-lang/rust/blob/1.54.0/library/core/src/hash/mod.rs#L717-L736
+// So these are OK:
+fn raw_ptr_is_ok(_m: &mut HashMap<*const Key, ()>) {}
+fn raw_mut_ptr_is_ok(_m: &mut HashMap<*mut Key, ()>) {}
+
 #[allow(unused)]
 trait Trait {
     type AssociatedType;
 
-    fn trait_fn(&self, set: std::collections::HashSet<Self::AssociatedType>);
+    fn trait_fn(&self, set: HashSet<Self::AssociatedType>);
 }
 
 fn generics_are_ok_too<K>(_m: &mut HashSet<K>) {
@@ -52,4 +63,23 @@ fn main() {
     tuples::<Key>(&mut HashMap::new());
     tuples::<()>(&mut HashMap::new());
     tuples_bad::<()>(&mut HashMap::new());
+
+    raw_ptr_is_ok(&mut HashMap::new());
+    raw_mut_ptr_is_ok(&mut HashMap::new());
+
+    let _map = HashMap::<Cell<usize>, usize>::new();
+    let _map = HashMap::<&mut Cell<usize>, usize>::new();
+    let _map = HashMap::<&mut usize, usize>::new();
+    // Collection types from `std` who's impl of `Hash` or `Ord` delegate their type parameters
+    let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
+    let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
+    let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
+    let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
+    let _map = HashMap::<Option<Cell<usize>>, usize>::new();
+    let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
+    let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
+    // Smart pointers from `std` who's impl of `Hash` or `Ord` delegate their type parameters
+    let _map = HashMap::<Box<Cell<usize>>, usize>::new();
+    let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
+    let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
 }
index a8460b06ca6038ef3b976e9b6867ca33049cfcc1..25dd029b16eeea1e13b5e773613ddf27e106bd4e 100644 (file)
@@ -1,5 +1,5 @@
 error: mutable key type
-  --> $DIR/mut_key.rs:27:32
+  --> $DIR/mut_key.rs:30:32
    |
 LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,22 +7,100 @@ LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> Hash
    = note: `-D clippy::mutable-key-type` implied by `-D warnings`
 
 error: mutable key type
-  --> $DIR/mut_key.rs:27:72
+  --> $DIR/mut_key.rs:30:72
    |
 LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
    |                                                                        ^^^^^^^^^^^^
 
 error: mutable key type
-  --> $DIR/mut_key.rs:28:5
+  --> $DIR/mut_key.rs:31:5
    |
 LL |     let _other: HashMap<Key, bool> = HashMap::new();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: mutable key type
-  --> $DIR/mut_key.rs:47:22
+  --> $DIR/mut_key.rs:58:22
    |
 LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: mutable key type
+  --> $DIR/mut_key.rs:70:5
+   |
+LL |     let _map = HashMap::<Cell<usize>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:71:5
+   |
+LL |     let _map = HashMap::<&mut Cell<usize>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:72:5
+   |
+LL |     let _map = HashMap::<&mut usize, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:74:5
+   |
+LL |     let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:75:5
+   |
+LL |     let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:76:5
+   |
+LL |     let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:77:5
+   |
+LL |     let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:78:5
+   |
+LL |     let _map = HashMap::<Option<Cell<usize>>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:79:5
+   |
+LL |     let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:80:5
+   |
+LL |     let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:82:5
+   |
+LL |     let _map = HashMap::<Box<Cell<usize>>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:83:5
+   |
+LL |     let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mutable key type
+  --> $DIR/mut_key.rs:84:5
+   |
+LL |     let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 17 previous errors
 
index a87171dc3f24d80a8abf684916ac58e39bd29e80..42c2bb9f4149eb48bbf2f777e859adcc88623ee0 100644 (file)
@@ -1,17 +1,16 @@
 // run-rustfix
 
-#![allow(clippy::needless_borrowed_reference)]
-
-fn x(y: &i32) -> i32 {
-    *y
-}
-
 #[warn(clippy::all, clippy::needless_borrow)]
 #[allow(unused_variables)]
 fn main() {
     let a = 5;
-    let b = x(&a);
-    let c = x(&a);
+    let _ = x(&a); // no warning
+    let _ = x(&a); // warn
+
+    let mut b = 5;
+    mut_ref(&mut b); // no warning
+    mut_ref(&mut b); // warn
+
     let s = &String::from("hi");
     let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
     let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
@@ -29,6 +28,15 @@ fn main() {
     };
 }
 
+#[allow(clippy::needless_borrowed_reference)]
+fn x(y: &i32) -> i32 {
+    *y
+}
+
+fn mut_ref(y: &mut i32) {
+    *y = 5;
+}
+
 fn f<T: Copy>(y: &T) -> T {
     *y
 }
index 059dc8ceac31a1521aabc2928d80b9b129d7b003..31977416bc7028738fb91c94c0a3d6dd0af4c9f9 100644 (file)
@@ -1,17 +1,16 @@
 // run-rustfix
 
-#![allow(clippy::needless_borrowed_reference)]
-
-fn x(y: &i32) -> i32 {
-    *y
-}
-
 #[warn(clippy::all, clippy::needless_borrow)]
 #[allow(unused_variables)]
 fn main() {
     let a = 5;
-    let b = x(&a);
-    let c = x(&&a);
+    let _ = x(&a); // no warning
+    let _ = x(&&a); // warn
+
+    let mut b = 5;
+    mut_ref(&mut b); // no warning
+    mut_ref(&mut &mut b); // warn
+
     let s = &String::from("hi");
     let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
     let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
@@ -29,6 +28,15 @@ fn main() {
     };
 }
 
+#[allow(clippy::needless_borrowed_reference)]
+fn x(y: &i32) -> i32 {
+    *y
+}
+
+fn mut_ref(y: &mut i32) {
+    *y = 5;
+}
+
 fn f<T: Copy>(y: &T) -> T {
     *y
 }
index 6eddf26db068f8fa58f3f89894f2ca3cd6d02978..012d62e287156f850f02871bba101d930a9bbdb7 100644 (file)
@@ -1,16 +1,22 @@
 error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
-  --> $DIR/needless_borrow.rs:14:15
+  --> $DIR/needless_borrow.rs:8:15
    |
-LL |     let c = x(&&a);
+LL |     let _ = x(&&a); // warn
    |               ^^^ help: change this to: `&a`
    |
    = note: `-D clippy::needless-borrow` implied by `-D warnings`
 
+error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:12:13
+   |
+LL |     mut_ref(&mut &mut b); // warn
+   |             ^^^^^^^^^^^ help: change this to: `&mut b`
+
 error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
-  --> $DIR/needless_borrow.rs:27:15
+  --> $DIR/needless_borrow.rs:26:15
    |
 LL |         46 => &&a,
    |               ^^^ help: change this to: `&a`
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
index 7a9ba55590dce0c0154ead2ef1c5506915fda7e6..5a35b100afe07620e234479856f2ccae4500cf1e 100644 (file)
@@ -3,7 +3,6 @@
     dead_code,
     clippy::single_match,
     clippy::redundant_pattern_matching,
-    clippy::many_single_char_names,
     clippy::option_option,
     clippy::redundant_clone
 )]
index 2f61ba241c45dd64f2bb13c9e6a142ab342fd796..d960c86a9f0ef257e1bc27ca0ae63d9335d14c5f 100644 (file)
@@ -1,5 +1,5 @@
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:18:23
+  --> $DIR/needless_pass_by_value.rs:17:23
    |
 LL | fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> {
    |                       ^^^^^^ help: consider changing the type to: `&[T]`
@@ -7,55 +7,55 @@ LL | fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T
    = note: `-D clippy::needless-pass-by-value` implied by `-D warnings`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:32:11
+  --> $DIR/needless_pass_by_value.rs:31:11
    |
 LL | fn bar(x: String, y: Wrapper) {
    |           ^^^^^^ help: consider changing the type to: `&str`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:32:22
+  --> $DIR/needless_pass_by_value.rs:31:22
    |
 LL | fn bar(x: String, y: Wrapper) {
    |                      ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:38:71
+  --> $DIR/needless_pass_by_value.rs:37:71
    |
 LL | fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) {
    |                                                                       ^ help: consider taking a reference instead: `&V`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:50:18
+  --> $DIR/needless_pass_by_value.rs:49:18
    |
 LL | fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) {
    |                  ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option<Option<String>>`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:63:24
+  --> $DIR/needless_pass_by_value.rs:62:24
    |
 LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
    |                        ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:63:36
+  --> $DIR/needless_pass_by_value.rs:62:36
    |
 LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) {
    |                                    ^^^^^^^ help: consider taking a reference instead: `&Wrapper`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:79:49
+  --> $DIR/needless_pass_by_value.rs:78:49
    |
 LL | fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {}
    |                                                 ^ help: consider taking a reference instead: `&T`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:81:18
+  --> $DIR/needless_pass_by_value.rs:80:18
    |
 LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
    |                  ^^^^^^ help: consider taking a reference instead: `&String`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:81:29
+  --> $DIR/needless_pass_by_value.rs:80:29
    |
 LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
    |                             ^^^^^^
@@ -70,13 +70,13 @@ LL |     let _ = t.to_string();
    |             ~~~~~~~~~~~~~
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:81:40
+  --> $DIR/needless_pass_by_value.rs:80:40
    |
 LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
    |                                        ^^^^^^^^ help: consider taking a reference instead: `&Vec<i32>`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:81:53
+  --> $DIR/needless_pass_by_value.rs:80:53
    |
 LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) {
    |                                                     ^^^^^^^^
@@ -91,85 +91,85 @@ LL |     let _ = v.to_owned();
    |             ~~~~~~~~~~~~
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:94:12
+  --> $DIR/needless_pass_by_value.rs:93:12
    |
 LL |         s: String,
    |            ^^^^^^ help: consider changing the type to: `&str`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:95:12
+  --> $DIR/needless_pass_by_value.rs:94:12
    |
 LL |         t: String,
    |            ^^^^^^ help: consider taking a reference instead: `&String`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:104:23
+  --> $DIR/needless_pass_by_value.rs:103:23
    |
 LL |     fn baz(&self, _u: U, _s: Self) {}
    |                       ^ help: consider taking a reference instead: `&U`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:104:30
+  --> $DIR/needless_pass_by_value.rs:103:30
    |
 LL |     fn baz(&self, _u: U, _s: Self) {}
    |                              ^^^^ help: consider taking a reference instead: `&Self`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:126:24
+  --> $DIR/needless_pass_by_value.rs:125:24
    |
 LL | fn bar_copy(x: u32, y: CopyWrapper) {
    |                        ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
    |
 help: consider marking this type as `Copy`
-  --> $DIR/needless_pass_by_value.rs:124:1
+  --> $DIR/needless_pass_by_value.rs:123:1
    |
 LL | struct CopyWrapper(u32);
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:132:29
+  --> $DIR/needless_pass_by_value.rs:131:29
    |
 LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
    |                             ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
    |
 help: consider marking this type as `Copy`
-  --> $DIR/needless_pass_by_value.rs:124:1
+  --> $DIR/needless_pass_by_value.rs:123:1
    |
 LL | struct CopyWrapper(u32);
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:132:45
+  --> $DIR/needless_pass_by_value.rs:131:45
    |
 LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
    |                                             ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
    |
 help: consider marking this type as `Copy`
-  --> $DIR/needless_pass_by_value.rs:124:1
+  --> $DIR/needless_pass_by_value.rs:123:1
    |
 LL | struct CopyWrapper(u32);
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:132:61
+  --> $DIR/needless_pass_by_value.rs:131:61
    |
 LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) {
    |                                                             ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper`
    |
 help: consider marking this type as `Copy`
-  --> $DIR/needless_pass_by_value.rs:124:1
+  --> $DIR/needless_pass_by_value.rs:123:1
    |
 LL | struct CopyWrapper(u32);
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:144:40
+  --> $DIR/needless_pass_by_value.rs:143:40
    |
 LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {}
    |                                        ^ help: consider taking a reference instead: `&S`
 
 error: this argument is passed by value, but not consumed in the function body
-  --> $DIR/needless_pass_by_value.rs:149:20
+  --> $DIR/needless_pass_by_value.rs:148:20
    |
 LL | fn more_fun(_item: impl Club<'static, i32>) {}
    |                    ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>`
index 5c4fd466c04187df5abf52be6a504374c59c919b..37efa6274df7aa904b65fc99b458ad60acc4dfef 100644 (file)
@@ -1,13 +1,9 @@
 // run-rustfix
 // edition:2018
 
+#![feature(let_else)]
 #![allow(unused)]
-#![allow(
-    clippy::if_same_then_else,
-    clippy::single_match,
-    clippy::branches_sharing_code,
-    clippy::needless_bool
-)]
+#![allow(clippy::if_same_then_else, clippy::single_match, clippy::needless_bool)]
 #![warn(clippy::needless_return)]
 
 macro_rules! the_answer {
@@ -207,4 +203,8 @@ async fn async_test_return_in_macro() {
     needed_return!(0);
 }
 
+fn let_else() {
+    let Some(1) = Some(1) else { return };
+}
+
 fn main() {}
index 34811db7413a3ac5d90254dd7591c02b53b6556b..cbf384ac9e4356a21548e5181947da79a0b95ad2 100644 (file)
@@ -1,13 +1,9 @@
 // run-rustfix
 // edition:2018
 
+#![feature(let_else)]
 #![allow(unused)]
-#![allow(
-    clippy::if_same_then_else,
-    clippy::single_match,
-    clippy::branches_sharing_code,
-    clippy::needless_bool
-)]
+#![allow(clippy::if_same_then_else, clippy::single_match, clippy::needless_bool)]
 #![warn(clippy::needless_return)]
 
 macro_rules! the_answer {
@@ -207,4 +203,8 @@ async fn async_test_return_in_macro() {
     needed_return!(0);
 }
 
+fn let_else() {
+    let Some(1) = Some(1) else { return };
+}
+
 fn main() {}
index 74dda971fdabb632633f4a125ba15d6e2d5ad1a3..7ce7028bbae4b1defd812911afb7a4a540d8b7fe 100644 (file)
@@ -1,5 +1,5 @@
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:24:5
+  --> $DIR/needless_return.rs:20:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
@@ -7,187 +7,187 @@ LL |     return true;
    = note: `-D clippy::needless-return` implied by `-D warnings`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:28:5
+  --> $DIR/needless_return.rs:24:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:33:9
+  --> $DIR/needless_return.rs:29:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:35:9
+  --> $DIR/needless_return.rs:31:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:41:17
+  --> $DIR/needless_return.rs:37:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:43:13
+  --> $DIR/needless_return.rs:39:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:50:9
+  --> $DIR/needless_return.rs:46:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:52:16
+  --> $DIR/needless_return.rs:48:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:60:5
+  --> $DIR/needless_return.rs:56:5
    |
 LL |     return;
    |     ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:65:9
+  --> $DIR/needless_return.rs:61:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:67:9
+  --> $DIR/needless_return.rs:63:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:74:14
+  --> $DIR/needless_return.rs:70:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:89:9
+  --> $DIR/needless_return.rs:85:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:91:9
+  --> $DIR/needless_return.rs:87:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:112:32
+  --> $DIR/needless_return.rs:108:32
    |
 LL |         bar.unwrap_or_else(|_| return)
    |                                ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:117:13
+  --> $DIR/needless_return.rs:113:13
    |
 LL |             return;
    |             ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:119:20
+  --> $DIR/needless_return.rs:115:20
    |
 LL |         let _ = || return;
    |                    ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:125:32
+  --> $DIR/needless_return.rs:121:32
    |
 LL |         res.unwrap_or_else(|_| return Foo)
    |                                ^^^^^^^^^^ help: remove `return`: `Foo`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:134:5
+  --> $DIR/needless_return.rs:130:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:138:5
+  --> $DIR/needless_return.rs:134:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:143:9
+  --> $DIR/needless_return.rs:139:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:145:9
+  --> $DIR/needless_return.rs:141:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:151:17
+  --> $DIR/needless_return.rs:147:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:153:13
+  --> $DIR/needless_return.rs:149:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:160:9
+  --> $DIR/needless_return.rs:156:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:162:16
+  --> $DIR/needless_return.rs:158:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:170:5
+  --> $DIR/needless_return.rs:166:5
    |
 LL |     return;
    |     ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:175:9
+  --> $DIR/needless_return.rs:171:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:177:9
+  --> $DIR/needless_return.rs:173:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:184:14
+  --> $DIR/needless_return.rs:180:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:199:9
+  --> $DIR/needless_return.rs:195:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:201:9
+  --> $DIR/needless_return.rs:197:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
index 971be26278f3c4276e2d8b0b1490cd62f4902d96..fa5743c11557722fecbf1216143d3b8e58b9dd20 100644 (file)
@@ -1,4 +1,4 @@
-#![allow(unused, clippy::many_single_char_names, clippy::diverging_sub_expression)]
+#![allow(unused, clippy::diverging_sub_expression)]
 #![warn(clippy::nonminimal_bool)]
 
 fn main() {
index 907587402908a7bf09ec65e1bead808c9426239b..d0a289b7ea43e1f88ec324c33cdbd9f11c61a406 100644 (file)
@@ -1,4 +1,4 @@
-#![allow(unused, clippy::many_single_char_names, clippy::diverging_sub_expression)]
+#![allow(unused, clippy::diverging_sub_expression)]
 #![warn(clippy::nonminimal_bool)]
 
 fn methods_with_negation() {
index 6605c967c8e7e6323d659f0a7c645e0cd59832c2..ab9c4d34c88f6200faf8db7ed94946b7d9b724f0 100644 (file)
@@ -1,6 +1,5 @@
 #![allow(unused_variables, clippy::blacklisted_name)]
 #![warn(clippy::op_ref)]
-#![allow(clippy::many_single_char_names)]
 use std::collections::HashSet;
 use std::ops::BitAnd;
 
index 821099d8779dc1421b5f6625ad67e0b3e220f29f..992417084bda29499222b3036bcb8a8ca86dd029 100644 (file)
@@ -1,5 +1,5 @@
 error: needlessly taken reference of both operands
-  --> $DIR/op_ref.rs:12:15
+  --> $DIR/op_ref.rs:11:15
    |
 LL |     let foo = &5 - &6;
    |               ^^^^^^^
@@ -11,7 +11,7 @@ LL |     let foo = 5 - 6;
    |               ~   ~
 
 error: taken reference of right operand
-  --> $DIR/op_ref.rs:57:13
+  --> $DIR/op_ref.rs:56:13
    |
 LL |     let z = x & &y;
    |             ^^^^--
index 84332040dbadbdb1e55bd34e2c696d5a1d7f2ba1..5db75f5291becdaa4d517874db275aa5d6cb4973 100644 (file)
@@ -1,4 +1,3 @@
-#![allow(clippy::many_single_char_names)]
 #![warn(clippy::overflow_check_conditional)]
 
 fn main() {
index 19e843c2c0a50d5b468a9a2a4447db3bcac7f6e2..1b8b146b60ae72558f14c5a2cb34d0272ad39276 100644 (file)
@@ -1,5 +1,5 @@
 error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:8:8
+  --> $DIR/overflow_check_conditional.rs:7:8
    |
 LL |     if a + b < a {}
    |        ^^^^^^^^^
@@ -7,43 +7,43 @@ LL |     if a + b < a {}
    = note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
 
 error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:9:8
+  --> $DIR/overflow_check_conditional.rs:8:8
    |
 LL |     if a > a + b {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:10:8
+  --> $DIR/overflow_check_conditional.rs:9:8
    |
 LL |     if a + b < b {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:11:8
+  --> $DIR/overflow_check_conditional.rs:10:8
    |
 LL |     if b > a + b {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:12:8
+  --> $DIR/overflow_check_conditional.rs:11:8
    |
 LL |     if a - b > b {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:13:8
+  --> $DIR/overflow_check_conditional.rs:12:8
    |
 LL |     if b < a - b {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:14:8
+  --> $DIR/overflow_check_conditional.rs:13:8
    |
 LL |     if a - b > a {}
    |        ^^^^^^^^^
 
 error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> $DIR/overflow_check_conditional.rs:15:8
+  --> $DIR/overflow_check_conditional.rs:14:8
    |
 LL |     if a < a - b {}
    |        ^^^^^^^^^
index 06370dfce65188899dda939fb0dbee8e561c7170..99e6d2aad8dd6b46ac5229da0866d36218688f0a 100644 (file)
@@ -1,4 +1,9 @@
-#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+#![allow(
+    unused,
+    clippy::many_single_char_names,
+    clippy::redundant_clone,
+    clippy::if_then_panic
+)]
 #![warn(clippy::ptr_arg)]
 
 use std::borrow::Cow;
index 64594eb870c2c5067b413095f6eabdeded968ebc..42183447ead737cf34275a3fe9be5edf01a406c4 100644 (file)
@@ -1,5 +1,5 @@
 error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
-  --> $DIR/ptr_arg.rs:7:14
+  --> $DIR/ptr_arg.rs:12:14
    |
 LL | fn do_vec(x: &Vec<i64>) {
    |              ^^^^^^^^^ help: change this to: `&[i64]`
@@ -7,25 +7,25 @@ LL | fn do_vec(x: &Vec<i64>) {
    = note: `-D clippy::ptr-arg` implied by `-D warnings`
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:16:14
+  --> $DIR/ptr_arg.rs:21:14
    |
 LL | fn do_str(x: &String) {
    |              ^^^^^^^ help: change this to: `&str`
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:25:15
+  --> $DIR/ptr_arg.rs:30:15
    |
 LL | fn do_path(x: &PathBuf) {
    |               ^^^^^^^^ help: change this to: `&Path`
 
 error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
-  --> $DIR/ptr_arg.rs:38:18
+  --> $DIR/ptr_arg.rs:43:18
    |
 LL |     fn do_vec(x: &Vec<i64>);
    |                  ^^^^^^^^^ help: change this to: `&[i64]`
 
 error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
-  --> $DIR/ptr_arg.rs:51:14
+  --> $DIR/ptr_arg.rs:56:14
    |
 LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
    |              ^^^^^^^^
@@ -44,7 +44,7 @@ LL |     x.to_owned()
    |
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:60:18
+  --> $DIR/ptr_arg.rs:65:18
    |
 LL | fn str_cloned(x: &String) -> String {
    |                  ^^^^^^^
@@ -67,7 +67,7 @@ LL |     x.to_string()
    |
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:68:19
+  --> $DIR/ptr_arg.rs:73:19
    |
 LL | fn path_cloned(x: &PathBuf) -> PathBuf {
    |                   ^^^^^^^^
@@ -90,7 +90,7 @@ LL |     x.to_path_buf()
    |
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:76:44
+  --> $DIR/ptr_arg.rs:81:44
    |
 LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
    |                                            ^^^^^^^
@@ -109,13 +109,13 @@ LL |     let c = y;
    |             ~
 
 error: using a reference to `Cow` is not recommended
-  --> $DIR/ptr_arg.rs:90:25
+  --> $DIR/ptr_arg.rs:95:25
    |
 LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
    |                         ^^^^^^^^^^^ help: change this to: `&[i32]`
 
 error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices
-  --> $DIR/ptr_arg.rs:143:21
+  --> $DIR/ptr_arg.rs:148:21
    |
 LL |     fn foo_vec(vec: &Vec<u8>) {
    |                     ^^^^^^^^
@@ -134,7 +134,7 @@ LL |         let _ = vec.to_owned().clone();
    |                 ~~~~~~~~~~~~~~
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:148:23
+  --> $DIR/ptr_arg.rs:153:23
    |
 LL |     fn foo_path(path: &PathBuf) {
    |                       ^^^^^^^^
@@ -153,7 +153,7 @@ LL |         let _ = path.to_path_buf().clone();
    |                 ~~~~~~~~~~~~~~~~~~
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:153:21
+  --> $DIR/ptr_arg.rs:158:21
    |
 LL |     fn foo_str(str: &PathBuf) {
    |                     ^^^^^^^^
index a637c22fbcd266211e9247df2ac21c2b14b3dd56..dc197e50300d8b11939db42d3ee7738bdb9fa9f7 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 #![warn(clippy::repeat_once)]
-#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+#[allow(unused, clippy::redundant_clone)]
 fn main() {
     const N: usize = 1;
     let s = "str";
index d99ca1b5b55d4200df4831fa22074cc3f2ce39cb..0ec5127117c6efab7484649994c9775cb68105e6 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 #![warn(clippy::repeat_once)]
-#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+#[allow(unused, clippy::redundant_clone)]
 fn main() {
     const N: usize = 1;
     let s = "str";
diff --git a/src/tools/clippy/tests/ui/same_name_method.rs b/src/tools/clippy/tests/ui/same_name_method.rs
new file mode 100644 (file)
index 0000000..12e10ba
--- /dev/null
@@ -0,0 +1,111 @@
+#![warn(clippy::same_name_method)]
+#![allow(dead_code, non_camel_case_types)]
+
+trait T1 {
+    fn foo() {}
+}
+
+trait T2 {
+    fn foo() {}
+}
+
+mod should_lint {
+
+    mod test_basic_case {
+        use crate::T1;
+
+        struct S;
+
+        impl S {
+            fn foo() {}
+        }
+
+        impl T1 for S {
+            fn foo() {}
+        }
+    }
+
+    mod test_derive {
+
+        #[derive(Clone)]
+        struct S;
+
+        impl S {
+            fn clone() {}
+        }
+    }
+
+    mod with_generic {
+        use crate::T1;
+
+        struct S<U>(U);
+
+        impl<U> S<U> {
+            fn foo() {}
+        }
+
+        impl<U: Copy> T1 for S<U> {
+            fn foo() {}
+        }
+    }
+
+    mod default_method {
+        use crate::T1;
+
+        struct S;
+
+        impl S {
+            fn foo() {}
+        }
+
+        impl T1 for S {}
+    }
+
+    mod mulitply_conflicit_trait {
+        use crate::{T1, T2};
+
+        struct S;
+
+        impl S {
+            fn foo() {}
+        }
+
+        impl T1 for S {}
+
+        impl T2 for S {}
+    }
+}
+
+mod should_not_lint {
+
+    mod not_lint_two_trait_method {
+        use crate::{T1, T2};
+
+        struct S;
+
+        impl T1 for S {
+            fn foo() {}
+        }
+
+        impl T2 for S {
+            fn foo() {}
+        }
+    }
+
+    mod only_lint_on_method {
+        trait T3 {
+            type foo;
+        }
+
+        struct S;
+
+        impl S {
+            fn foo() {}
+        }
+        impl T3 for S {
+            type foo = usize;
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/same_name_method.stderr b/src/tools/clippy/tests/ui/same_name_method.stderr
new file mode 100644 (file)
index 0000000..0f9139b
--- /dev/null
@@ -0,0 +1,64 @@
+error: method's name is same to an existing method in a trait
+  --> $DIR/same_name_method.rs:20:13
+   |
+LL |             fn foo() {}
+   |             ^^^^^^^^^^^
+   |
+   = note: `-D clippy::same-name-method` implied by `-D warnings`
+note: existing `foo` defined here
+  --> $DIR/same_name_method.rs:24:13
+   |
+LL |             fn foo() {}
+   |             ^^^^^^^^^^^
+
+error: method's name is same to an existing method in a trait
+  --> $DIR/same_name_method.rs:44:13
+   |
+LL |             fn foo() {}
+   |             ^^^^^^^^^^^
+   |
+note: existing `foo` defined here
+  --> $DIR/same_name_method.rs:48:13
+   |
+LL |             fn foo() {}
+   |             ^^^^^^^^^^^
+
+error: method's name is same to an existing method in a trait
+  --> $DIR/same_name_method.rs:58:13
+   |
+LL |             fn foo() {}
+   |             ^^^^^^^^^^^
+   |
+note: existing `foo` defined here
+  --> $DIR/same_name_method.rs:61:9
+   |
+LL |         impl T1 for S {}
+   |         ^^^^^^^^^^^^^^^^
+
+error: method's name is same to an existing method in a trait
+  --> $DIR/same_name_method.rs:70:13
+   |
+LL |             fn foo() {}
+   |             ^^^^^^^^^^^
+   |
+note: existing `foo` defined here
+  --> $DIR/same_name_method.rs:73:9
+   |
+LL |         impl T1 for S {}
+   |         ^^^^^^^^^^^^^^^^
+
+error: method's name is same to an existing method in a trait
+  --> $DIR/same_name_method.rs:34:13
+   |
+LL |             fn clone() {}
+   |             ^^^^^^^^^^^^^
+   |
+note: existing `clone` defined here
+  --> $DIR/same_name_method.rs:30:18
+   |
+LL |         #[derive(Clone)]
+   |                  ^^^^^
+   = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 5 previous errors
+
index 79ba7402f1f49285a79b0de83952e5f46c272333..9644a23296831bf1a67092feecba921180aa3ba7 100644 (file)
@@ -1,4 +1,5 @@
 #![warn(clippy::semicolon_if_nothing_returned)]
+#![allow(clippy::redundant_closure)]
 #![feature(label_break_value)]
 
 fn get_unit() {}
@@ -30,8 +31,8 @@ fn unsafe_checks_error() {
     use std::ptr;
 
     let mut s = MaybeUninit::<String>::uninit();
-    let _d = || unsafe { 
-        ptr::drop_in_place(s.as_mut_ptr()) 
+    let _d = || unsafe {
+        ptr::drop_in_place(s.as_mut_ptr())
     };
 }
 
index e88ebe2ad35f0bcacf64ed5f8ee3bc57e1227ae7..78813e7cc1c39c5aa294f08db14ca9cfe0688a50 100644 (file)
@@ -1,5 +1,5 @@
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:8:5
+  --> $DIR/semicolon_if_nothing_returned.rs:9:5
    |
 LL |     println!("Hello")
    |     ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");`
@@ -7,27 +7,27 @@ LL |     println!("Hello")
    = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:12:5
+  --> $DIR/semicolon_if_nothing_returned.rs:13:5
    |
 LL |     get_unit()
    |     ^^^^^^^^^^ help: add a `;` here: `get_unit();`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:17:5
+  --> $DIR/semicolon_if_nothing_returned.rs:18:5
    |
 LL |     y = x + 1
    |     ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:23:9
+  --> $DIR/semicolon_if_nothing_returned.rs:24:9
    |
 LL |         hello()
    |         ^^^^^^^ help: add a `;` here: `hello();`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:34:9
+  --> $DIR/semicolon_if_nothing_returned.rs:35:9
    |
-LL |         ptr::drop_in_place(s.as_mut_ptr()) 
+LL |         ptr::drop_in_place(s.as_mut_ptr())
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`
 
 error: aborting due to 5 previous errors
index 547615b10d9fb11741e2d9e59a1dbc2cc26f53cd..be8bc22bf98a05d062caff083c4c6e1d1d903c85 100644 (file)
@@ -1,5 +1,10 @@
+// aux-build:proc_macro_suspicious_else_formatting.rs
+
 #![warn(clippy::suspicious_else_formatting)]
 
+extern crate proc_macro_suspicious_else_formatting;
+use proc_macro_suspicious_else_formatting::DeriveBadSpan;
+
 fn foo() -> bool {
     true
 }
@@ -103,3 +108,7 @@ fn main() {
     {
     }
 }
+
+// #7650 - Don't lint. Proc-macro using bad spans for `if` expressions.
+#[derive(DeriveBadSpan)]
+struct _Foo(u32, u32);
index d8d67b4138ab3b95c9f107c4265ead6548416846..d1db195cbb8787bc0003738f2f2c1e37adbf7fe3 100644 (file)
@@ -1,5 +1,5 @@
 error: this looks like an `else {..}` but the `else` is missing
-  --> $DIR/suspicious_else_formatting.rs:11:6
+  --> $DIR/suspicious_else_formatting.rs:16:6
    |
 LL |     } {
    |      ^
@@ -8,7 +8,7 @@ LL |     } {
    = note: to remove this lint, add the missing `else` or add a new line before the next block
 
 error: this looks like an `else if` but the `else` is missing
-  --> $DIR/suspicious_else_formatting.rs:15:6
+  --> $DIR/suspicious_else_formatting.rs:20:6
    |
 LL |     } if foo() {
    |      ^
@@ -16,7 +16,7 @@ LL |     } if foo() {
    = note: to remove this lint, add the missing `else` or add a new line before the second `if`
 
 error: this looks like an `else if` but the `else` is missing
-  --> $DIR/suspicious_else_formatting.rs:22:10
+  --> $DIR/suspicious_else_formatting.rs:27:10
    |
 LL |         } if foo() {
    |          ^
@@ -24,7 +24,7 @@ LL |         } if foo() {
    = note: to remove this lint, add the missing `else` or add a new line before the second `if`
 
 error: this looks like an `else if` but the `else` is missing
-  --> $DIR/suspicious_else_formatting.rs:30:10
+  --> $DIR/suspicious_else_formatting.rs:35:10
    |
 LL |         } if foo() {
    |          ^
@@ -32,7 +32,7 @@ LL |         } if foo() {
    = note: to remove this lint, add the missing `else` or add a new line before the second `if`
 
 error: this is an `else {..}` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:39:6
+  --> $DIR/suspicious_else_formatting.rs:44:6
    |
 LL |       } else
    |  ______^
@@ -42,7 +42,7 @@ LL | |     {
    = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
 
 error: this is an `else if` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:51:6
+  --> $DIR/suspicious_else_formatting.rs:56:6
    |
 LL |       } else
    |  ______^
@@ -52,7 +52,7 @@ LL | |     if foo() { // the span of the above error should continue here
    = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
 
 error: this is an `else if` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:56:6
+  --> $DIR/suspicious_else_formatting.rs:61:6
    |
 LL |       }
    |  ______^
@@ -63,7 +63,7 @@ LL | |     if foo() { // the span of the above error should continue here
    = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
 
 error: this is an `else {..}` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:83:6
+  --> $DIR/suspicious_else_formatting.rs:88:6
    |
 LL |       }
    |  ______^
@@ -75,7 +75,7 @@ LL | |     {
    = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
 
 error: this is an `else {..}` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:91:6
+  --> $DIR/suspicious_else_formatting.rs:96:6
    |
 LL |       }
    |  ______^
index 1a0123803a3ee9f482baf3d7141630a143d766fc..ea3dce17081b1a5cee5b00cbe39de303ec03ed7b 100644 (file)
@@ -2,11 +2,7 @@
 // normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
 
 #![deny(clippy::trivially_copy_pass_by_ref)]
-#![allow(
-    clippy::many_single_char_names,
-    clippy::blacklisted_name,
-    clippy::redundant_field_names
-)]
+#![allow(clippy::blacklisted_name, clippy::redundant_field_names)]
 
 #[derive(Copy, Clone)]
 struct Foo(u32);
index 9c4c49ceac476f5f31ab13e24bef29d696ba3dc8..a88d35f3ea5a96f643e4031b3a66563ef49cb1d2 100644 (file)
@@ -1,5 +1,5 @@
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:51:11
+  --> $DIR/trivially_copy_pass_by_ref.rs:47:11
    |
 LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
    |           ^^^^ help: consider passing by value instead: `u32`
@@ -11,97 +11,97 @@ LL | #![deny(clippy::trivially_copy_pass_by_ref)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:51:20
+  --> $DIR/trivially_copy_pass_by_ref.rs:47:20
    |
 LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
    |                    ^^^^ help: consider passing by value instead: `Foo`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:51:29
+  --> $DIR/trivially_copy_pass_by_ref.rs:47:29
    |
 LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
    |                             ^^^^ help: consider passing by value instead: `Baz`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:58:12
+  --> $DIR/trivially_copy_pass_by_ref.rs:54:12
    |
 LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
    |            ^^^^^ help: consider passing by value instead: `self`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:58:22
+  --> $DIR/trivially_copy_pass_by_ref.rs:54:22
    |
 LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
    |                      ^^^^ help: consider passing by value instead: `u32`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:58:31
+  --> $DIR/trivially_copy_pass_by_ref.rs:54:31
    |
 LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
    |                               ^^^^ help: consider passing by value instead: `Foo`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:58:40
+  --> $DIR/trivially_copy_pass_by_ref.rs:54:40
    |
 LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
    |                                        ^^^^ help: consider passing by value instead: `Baz`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:60:16
+  --> $DIR/trivially_copy_pass_by_ref.rs:56:16
    |
 LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
    |                ^^^^ help: consider passing by value instead: `u32`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:60:25
+  --> $DIR/trivially_copy_pass_by_ref.rs:56:25
    |
 LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
    |                         ^^^^ help: consider passing by value instead: `Foo`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:60:34
+  --> $DIR/trivially_copy_pass_by_ref.rs:56:34
    |
 LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
    |                                  ^^^^ help: consider passing by value instead: `Baz`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:62:35
+  --> $DIR/trivially_copy_pass_by_ref.rs:58:35
    |
 LL |     fn bad_issue7518(self, other: &Self) {}
    |                                   ^^^^^ help: consider passing by value instead: `Self`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:74:16
+  --> $DIR/trivially_copy_pass_by_ref.rs:70:16
    |
 LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
    |                ^^^^ help: consider passing by value instead: `u32`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:74:25
+  --> $DIR/trivially_copy_pass_by_ref.rs:70:25
    |
 LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
    |                         ^^^^ help: consider passing by value instead: `Foo`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:74:34
+  --> $DIR/trivially_copy_pass_by_ref.rs:70:34
    |
 LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
    |                                  ^^^^ help: consider passing by value instead: `Baz`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:78:34
+  --> $DIR/trivially_copy_pass_by_ref.rs:74:34
    |
 LL |     fn trait_method(&self, _foo: &Foo);
    |                                  ^^^^ help: consider passing by value instead: `Foo`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:110:21
+  --> $DIR/trivially_copy_pass_by_ref.rs:106:21
    |
 LL |     fn foo_never(x: &i32) {
    |                     ^^^^ help: consider passing by value instead: `i32`
 
 error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-  --> $DIR/trivially_copy_pass_by_ref.rs:115:15
+  --> $DIR/trivially_copy_pass_by_ref.rs:111:15
    |
 LL |     fn foo(x: &i32) {
    |               ^^^^ help: consider passing by value instead: `i32`
index cdcdd808c94445f2d33b2771ded4f95db9091644..f5a341909023ee3429e902862b3344c4e1cdc050 100644 (file)
@@ -188,7 +188,7 @@ fn issue6491() {
     // Used in outer loop, needs &mut
     let mut it = 1..40;
     while let Some(n) = it.next() {
-        for m in &mut it {
+        for m in it.by_ref() {
             if m % 10 == 0 {
                 break;
             }
@@ -219,7 +219,7 @@ fn issue6491() {
 
         // Used after the loop, needs &mut.
         let mut it = 1..40;
-        for m in &mut it {
+        for m in it.by_ref() {
             if m % 10 == 0 {
                 break;
             }
@@ -236,7 +236,7 @@ fn issue6231() {
     let mut it = 1..40;
     let mut opt = Some(0);
     while let Some(n) = opt.take().or_else(|| it.next()) {
-        for m in &mut it {
+        for m in it.by_ref() {
             if n % 10 == 0 {
                 break;
             }
@@ -251,7 +251,7 @@ fn issue1924() {
     impl<T: Iterator<Item = u32>> S<T> {
         fn f(&mut self) -> Option<u32> {
             // Used as a field.
-            for i in &mut self.0 {
+            for i in self.0.by_ref() {
                 if !(3..=7).contains(&i) {
                     return Some(i);
                 }
@@ -283,7 +283,7 @@ fn issue1924() {
                 }
             }
             // This one is fine, a different field is borrowed
-            for i in &mut self.0.0.0 {
+            for i in self.0.0.0.by_ref() {
                 if i == 1 {
                     return self.0.1.take();
                 } else {
@@ -312,7 +312,7 @@ fn issue1924() {
 
     // Needs &mut, field of the iterator is accessed after the loop
     let mut it = S2(1..40, 0);
-    for n in &mut it {
+    for n in it.by_ref() {
         if n == 0 {
             break;
         }
@@ -324,7 +324,7 @@ fn issue7249() {
     let mut it = 0..10;
     let mut x = || {
         // Needs &mut, the closure can be called multiple times
-        for x in &mut it {
+        for x in it.by_ref() {
             if x % 2 == 0 {
                 break;
             }
@@ -338,7 +338,7 @@ fn issue7510() {
     let mut it = 0..10;
     let it = &mut it;
     // Needs to reborrow `it` as the binding isn't mutable
-    for x in &mut *it {
+    for x in it.by_ref() {
         if x % 2 == 0 {
             break;
         }
@@ -349,7 +349,7 @@ fn issue7510() {
     let mut it = 0..10;
     let it = S(&mut it);
     // Needs to reborrow `it.0` as the binding isn't mutable
-    for x in &mut *it.0 {
+    for x in it.0.by_ref() {
         if x % 2 == 0 {
             break;
         }
index ff9b08996da5316dc807b48e9d8175a1370aa05d..5e2fce4491af0956ff4ba97e6b96f9a85c230c35 100644 (file)
@@ -46,7 +46,7 @@ error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:191:9
    |
 LL |         while let Some(m) = it.next() {
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 
 error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:202:5
@@ -70,19 +70,19 @@ error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:222:9
    |
 LL |         while let Some(m) = it.next() {
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 
 error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:239:9
    |
 LL |         while let Some(m) = it.next() {
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 
 error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:254:13
    |
 LL |             while let Some(i) = self.0.next() {
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()`
 
 error: manual `!RangeInclusive::contains` implementation
   --> $DIR/while_let_on_iterator.rs:255:20
@@ -96,31 +96,31 @@ error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:286:13
    |
 LL |             while let Some(i) = self.0.0.0.next() {
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()`
 
 error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:315:5
    |
 LL |     while let Some(n) = it.next() {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()`
 
 error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:327:9
    |
 LL |         while let Some(x) = it.next() {
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
 
 error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:341:5
    |
 LL |     while let Some(x) = it.next() {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut *it`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
 
 error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:352:5
    |
 LL |     while let Some(x) = it.0.next() {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut *it.0`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()`
 
 error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:371:5
index 501bc1e6a85cbae27b04ebc7234644d4bbc12f0d..0d827c1feb3e722c4842684a441f6386ee2f2eb5 100644 (file)
@@ -68,3 +68,40 @@ trait Foo: Sized {
         fn as_byte_slice(slice: &[Self]) -> &[u8];
     }
 }
+
+mod issue3414 {
+    struct CellLikeThing<T>(T);
+
+    impl<T> CellLikeThing<T> {
+        // don't trigger
+        fn into_inner(this: Self) -> T {
+            this.0
+        }
+    }
+
+    impl<T> std::ops::Deref for CellLikeThing<T> {
+        type Target = T;
+
+        fn deref(&self) -> &T {
+            &self.0
+        }
+    }
+}
+
+// don't trigger
+mod issue4546 {
+    use std::pin::Pin;
+
+    struct S;
+    impl S {
+        pub fn as_mut(self: Pin<&mut Self>) {}
+
+        pub fn as_other_thingy(self: Pin<&Self>) {}
+
+        pub fn is_other_thingy(self: Pin<&Self>) {}
+
+        pub fn to_mut(self: Pin<&mut Self>) {}
+
+        pub fn to_other_thingy(self: Pin<&Self>) {}
+    }
+}
index f1d7f98ed07b9934286b9c4809dd4d7a47537879..009e6ceb1ddcd27a9ced3bcb7d0ef823379185a1 160000 (submodule)
@@ -1 +1 @@
-Subproject commit f1d7f98ed07b9934286b9c4809dd4d7a47537879
+Subproject commit 009e6ceb1ddcd27a9ced3bcb7d0ef823379185a1
index 35809e599266c8111e6758f84307b1371838ea69..a20ea3235ed46348fc07ac468a1c94c9c84b689f 100644 (file)
@@ -57,6 +57,7 @@ fn filter_dirs(path: &Path) -> bool {
     let skip = [
         "tidy-test-file",
         "compiler/rustc_codegen_cranelift",
+        "compiler/rustc_codegen_gcc",
         "src/llvm-project",
         "library/backtrace",
         "library/stdarch",