From 5869c6ff7a3bf934f0bcc4de49608749c877a3d5 Mon Sep 17 00:00:00 2001 From: Ximin Luo Date: Mon, 5 Apr 2021 22:17:21 +0100 Subject: [PATCH] New upstream version 1.51.0+dfsg1 --- Cargo.lock | 777 +- Cargo.toml | 8 +- README.md | 4 +- RELEASES.md | 171 + compiler/rustc_arena/src/lib.rs | 6 +- compiler/rustc_ast/src/ast.rs | 282 +- compiler/rustc_ast/src/attr/mod.rs | 21 +- compiler/rustc_ast/src/lib.rs | 1 + compiler/rustc_ast/src/mut_visit.rs | 45 +- compiler/rustc_ast/src/token.rs | 89 +- compiler/rustc_ast/src/tokenstream.rs | 50 +- compiler/rustc_ast/src/util/classify.rs | 18 +- compiler/rustc_ast/src/util/comments.rs | 6 +- compiler/rustc_ast/src/util/literal.rs | 12 +- compiler/rustc_ast/src/visit.rs | 35 +- compiler/rustc_ast_lowering/src/expr.rs | 106 +- compiler/rustc_ast_lowering/src/item.rs | 88 +- compiler/rustc_ast_lowering/src/lib.rs | 230 +- compiler/rustc_ast_lowering/src/path.rs | 15 +- .../rustc_ast_passes/src/ast_validation.rs | 129 +- compiler/rustc_ast_passes/src/feature_gate.rs | 76 +- compiler/rustc_ast_passes/src/lib.rs | 2 + compiler/rustc_ast_passes/src/node_count.rs | 2 +- compiler/rustc_ast_pretty/Cargo.toml | 1 - compiler/rustc_ast_pretty/src/lib.rs | 1 + compiler/rustc_ast_pretty/src/pp.rs | 4 +- compiler/rustc_ast_pretty/src/pprust/mod.rs | 5 - compiler/rustc_ast_pretty/src/pprust/state.rs | 66 +- compiler/rustc_attr/Cargo.toml | 1 - compiler/rustc_attr/src/builtin.rs | 48 +- compiler/rustc_builtin_macros/src/assert.rs | 26 +- .../src/deriving/clone.rs | 7 +- .../src/deriving/debug.rs | 38 +- .../src/deriving/generic/mod.rs | 24 +- .../rustc_builtin_macros/src/deriving/hash.rs | 10 +- .../rustc_builtin_macros/src/deriving/mod.rs | 9 +- compiler/rustc_builtin_macros/src/format.rs | 5 +- .../src/format_foreign.rs | 5 +- .../src/global_allocator.rs | 5 +- compiler/rustc_builtin_macros/src/lib.rs | 19 +- compiler/rustc_builtin_macros/src/llvm_asm.rs | 10 +- compiler/rustc_builtin_macros/src/panic.rs | 48 + .../src/proc_macro_harness.rs | 5 +- .../rustc_builtin_macros/src/source_util.rs | 5 +- compiler/rustc_builtin_macros/src/test.rs | 4 +- .../rustc_builtin_macros/src/test_harness.rs | 3 +- .../.github/workflows/main.yml | 5 +- .../.vscode/settings.json | 1 + compiler/rustc_codegen_cranelift/Cargo.lock | 122 +- compiler/rustc_codegen_cranelift/Cargo.toml | 10 +- compiler/rustc_codegen_cranelift/Readme.md | 21 +- compiler/rustc_codegen_cranelift/build.sh | 54 +- .../build_sysroot/Cargo.lock | 20 +- .../build_sysroot/Cargo.toml | 4 +- .../build_sysroot/build_sysroot.sh | 5 +- .../build_sysroot/prepare_sysroot_src.sh | 9 +- compiler/rustc_codegen_cranelift/clean_all.sh | 2 +- ...builtins-Remove-rotate_left-from-Int.patch | 35 + .../example/alloc_example.rs | 3 +- .../example/mini_core.rs | 4 +- .../example/mod_bench.rs | 3 +- .../example/std_example.rs | 2 + ...022-core-Disable-not-compiling-tests.patch | 16 + .../rustc_codegen_cranelift/rust-toolchain | 2 +- .../rustc_codegen_cranelift/scripts/cargo.sh | 4 +- .../rustc_codegen_cranelift/scripts/config.sh | 30 +- .../scripts/ext_config.sh | 27 + .../scripts/filter_profile.rs | 2 +- .../rustc_codegen_cranelift/scripts/tests.sh | 12 +- .../src/abi/comments.rs | 26 +- .../rustc_codegen_cranelift/src/abi/mod.rs | 398 +- .../src/abi/pass_mode.rs | 421 +- .../src/abi/returning.rs | 194 +- .../rustc_codegen_cranelift/src/analyze.rs | 11 +- .../rustc_codegen_cranelift/src/backend.rs | 4 +- compiler/rustc_codegen_cranelift/src/base.rs | 51 +- .../src/bin/cg_clif.rs | 32 +- .../src/bin/cg_clif_build_sysroot.rs | 9 +- .../src/codegen_i128.rs | 62 +- .../rustc_codegen_cranelift/src/common.rs | 57 +- .../rustc_codegen_cranelift/src/constant.rs | 16 +- .../src/debuginfo/emit.rs | 5 +- .../src/debuginfo/unwind.rs | 6 +- .../rustc_codegen_cranelift/src/driver/aot.rs | 38 +- .../rustc_codegen_cranelift/src/driver/jit.rs | 162 +- .../rustc_codegen_cranelift/src/driver/mod.rs | 65 +- .../src/intrinsics/llvm.rs | 4 +- .../src/intrinsics/mod.rs | 71 +- .../src/intrinsics/simd.rs | 25 +- compiler/rustc_codegen_cranelift/src/lib.rs | 113 +- .../rustc_codegen_cranelift/src/main_shim.rs | 4 +- compiler/rustc_codegen_cranelift/src/num.rs | 1 - .../src/optimize/peephole.rs | 39 +- .../src/pretty_clif.rs | 111 +- .../src/value_and_place.rs | 125 +- .../rustc_codegen_cranelift/src/vtable.rs | 3 +- compiler/rustc_codegen_cranelift/test.sh | 4 +- compiler/rustc_codegen_llvm/src/abi.rs | 14 +- compiler/rustc_codegen_llvm/src/attributes.rs | 40 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 5 +- compiler/rustc_codegen_llvm/src/back/write.rs | 40 +- compiler/rustc_codegen_llvm/src/base.rs | 18 +- compiler/rustc_codegen_llvm/src/builder.rs | 3 +- compiler/rustc_codegen_llvm/src/common.rs | 1 + compiler/rustc_codegen_llvm/src/consts.rs | 80 +- compiler/rustc_codegen_llvm/src/context.rs | 2 +- .../src/coverageinfo/mapgen.rs | 2 +- .../src/debuginfo/metadata.rs | 59 +- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 1 - compiler/rustc_codegen_llvm/src/lib.rs | 33 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 7 +- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 4 + compiler/rustc_codegen_llvm/src/llvm_util.rs | 56 +- compiler/rustc_codegen_llvm/src/type_.rs | 37 +- compiler/rustc_codegen_llvm/src/va_arg.rs | 16 +- compiler/rustc_codegen_ssa/Cargo.toml | 1 + compiler/rustc_codegen_ssa/src/back/link.rs | 132 +- compiler/rustc_codegen_ssa/src/back/linker.rs | 11 + compiler/rustc_codegen_ssa/src/back/write.rs | 20 +- compiler/rustc_codegen_ssa/src/base.rs | 139 +- .../rustc_codegen_ssa/src/coverageinfo/map.rs | 48 +- compiler/rustc_codegen_ssa/src/lib.rs | 5 +- compiler/rustc_codegen_ssa/src/mir/analyze.rs | 19 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 22 +- .../rustc_codegen_ssa/src/mir/constant.rs | 7 +- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 4 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 8 + compiler/rustc_codegen_ssa/src/mir/operand.rs | 25 +- compiler/rustc_codegen_ssa/src/mir/place.rs | 56 +- .../rustc_data_structures/src/fingerprint.rs | 26 +- .../src/graph/dominators/mod.rs | 16 +- .../src/graph/scc/mod.rs | 2 +- compiler/rustc_data_structures/src/lib.rs | 5 +- .../rustc_data_structures/src/profiling.rs | 55 +- .../src/stable_hasher.rs | 1 + compiler/rustc_data_structures/src/steal.rs | 5 +- compiler/rustc_driver/Cargo.toml | 1 + compiler/rustc_driver/src/lib.rs | 43 +- compiler/rustc_driver/src/pretty.rs | 11 +- compiler/rustc_error_codes/src/error_codes.rs | 4 +- .../src/error_codes/E0013.md | 2 +- .../src/error_codes/E0038.md | 2 +- .../src/error_codes/E0044.md | 6 +- .../src/error_codes/E0107.md | 2 +- .../src/error_codes/E0116.md | 2 +- .../src/error_codes/E0130.md | 6 +- .../src/error_codes/E0207.md | 2 +- .../src/error_codes/E0277.md | 4 +- .../src/error_codes/E0309.md | 2 +- .../src/error_codes/E0373.md | 21 + .../src/error_codes/E0435.md | 6 + .../src/error_codes/E0454.md | 4 +- .../src/error_codes/E0455.md | 4 +- .../src/error_codes/E0458.md | 2 +- .../src/error_codes/E0459.md | 4 +- .../src/error_codes/E0463.md | 21 + .../src/error_codes/E0492.md | 4 +- .../src/error_codes/E0521.md | 28 + .../src/error_codes/E0597.md | 4 +- .../src/error_codes/E0617.md | 4 +- .../src/error_codes/E0633.md | 2 +- .../src/error_codes/E0658.md | 4 +- .../src/error_codes/E0724.md | 2 +- .../src/error_codes/E0730.md | 2 - .../src/error_codes/E0754.md | 4 +- .../src/error_codes/E0759.md | 2 +- .../src/error_codes/E0770.md | 1 - .../src/error_codes/E0780.md | 19 + .../src/error_codes/E0781.md | 12 + .../rustc_errors/src/diagnostic_builder.rs | 10 +- compiler/rustc_errors/src/emitter.rs | 25 +- compiler/rustc_errors/src/lib.rs | 8 +- compiler/rustc_errors/src/snippet.rs | 15 +- compiler/rustc_errors/src/styled_buffer.rs | 27 +- compiler/rustc_expand/Cargo.toml | 1 + compiler/rustc_expand/src/base.rs | 57 +- compiler/rustc_expand/src/config.rs | 15 +- compiler/rustc_expand/src/expand.rs | 70 +- compiler/rustc_expand/src/mbe/macro_parser.rs | 26 +- compiler/rustc_expand/src/mbe/macro_rules.rs | 62 +- compiler/rustc_expand/src/mbe/quoted.rs | 74 +- compiler/rustc_expand/src/parse/tests.rs | 5 +- compiler/rustc_expand/src/placeholders.rs | 7 +- compiler/rustc_expand/src/proc_macro.rs | 7 +- .../rustc_expand/src/proc_macro_server.rs | 5 +- compiler/rustc_expand/src/tests.rs | 2 +- compiler/rustc_feature/src/accepted.rs | 2 + compiler/rustc_feature/src/active.rs | 34 +- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- compiler/rustc_feature/src/removed.rs | 3 + compiler/rustc_fs_util/src/lib.rs | 6 +- compiler/rustc_graphviz/src/tests.rs | 2 +- compiler/rustc_hir/Cargo.toml | 1 + compiler/rustc_hir/src/def.rs | 7 +- compiler/rustc_hir/src/definitions.rs | 4 + compiler/rustc_hir/src/hir.rs | 145 +- compiler/rustc_hir/src/intravisit.rs | 27 +- compiler/rustc_hir/src/lang_items.rs | 4 +- compiler/rustc_hir/src/lib.rs | 1 - compiler/rustc_hir/src/target.rs | 6 + compiler/rustc_hir/src/weak_lang_items.rs | 6 +- compiler/rustc_hir_pretty/src/lib.rs | 108 +- .../rustc_incremental/src/assert_dep_graph.rs | 8 +- compiler/rustc_incremental/src/lib.rs | 1 - .../src/persist/file_format.rs | 26 +- compiler/rustc_incremental/src/persist/fs.rs | 30 +- .../rustc_incremental/src/persist/load.rs | 9 - compiler/rustc_incremental/src/persist/mod.rs | 1 - .../rustc_incremental/src/persist/save.rs | 172 +- compiler/rustc_index/src/bit_set.rs | 12 + compiler/rustc_index/src/bit_set/tests.rs | 34 + .../src/infer/canonical/canonicalizer.rs | 12 +- .../src/infer/canonical/query_response.rs | 15 +- compiler/rustc_infer/src/infer/combine.rs | 9 +- .../src/infer/error_reporting/mod.rs | 94 +- .../infer/error_reporting/need_type_info.rs | 417 +- .../nice_region_error/static_impl_trait.rs | 9 +- .../trait_impl_difference.rs | 8 +- .../src/infer/error_reporting/note.rs | 64 +- .../rustc_infer/src/infer/free_regions.rs | 5 +- .../src/infer/lexical_region_resolve/mod.rs | 11 +- compiler/rustc_infer/src/infer/mod.rs | 18 +- .../rustc_infer/src/infer/outlives/mod.rs | 29 +- compiler/rustc_infer/src/infer/sub.rs | 2 +- compiler/rustc_infer/src/traits/util.rs | 37 +- compiler/rustc_interface/src/lib.rs | 1 + compiler/rustc_interface/src/passes.rs | 37 +- compiler/rustc_interface/src/queries.rs | 21 +- compiler/rustc_interface/src/tests.rs | 15 +- compiler/rustc_interface/src/util.rs | 4 +- compiler/rustc_lexer/src/cursor.rs | 2 +- compiler/rustc_lint/src/builtin.rs | 69 +- compiler/rustc_lint/src/context.rs | 65 +- compiler/rustc_lint/src/early.rs | 30 +- compiler/rustc_lint/src/internal.rs | 4 +- compiler/rustc_lint/src/late.rs | 4 + compiler/rustc_lint/src/levels.rs | 14 +- compiler/rustc_lint/src/lib.rs | 7 +- compiler/rustc_lint/src/non_fmt_panic.rs | 197 + compiler/rustc_lint/src/nonstandard_style.rs | 40 +- compiler/rustc_lint/src/panic_fmt.rs | 151 - .../rustc_lint/src/redundant_semicolon.rs | 20 +- compiler/rustc_lint/src/traits.rs | 4 +- compiler/rustc_lint/src/types.rs | 63 +- compiler/rustc_lint/src/unused.rs | 18 +- compiler/rustc_lint_defs/Cargo.toml | 1 + compiler/rustc_lint_defs/src/builtin.rs | 198 +- compiler/rustc_lint_defs/src/lib.rs | 3 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 16 +- compiler/rustc_llvm/src/lib.rs | 2 +- compiler/rustc_macros/src/query.rs | 165 +- compiler/rustc_macros/src/serialize.rs | 4 +- .../rustc_macros/src/session_diagnostic.rs | 2 +- compiler/rustc_metadata/src/creader.rs | 11 +- compiler/rustc_metadata/src/locator.rs | 24 +- compiler/rustc_metadata/src/native_libs.rs | 10 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 190 +- .../src/rmeta/decoder/cstore_impl.rs | 20 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 613 +- compiler/rustc_metadata/src/rmeta/mod.rs | 4 +- compiler/rustc_middle/Cargo.toml | 2 +- .../rustc_middle/src/dep_graph/dep_node.rs | 535 +- compiler/rustc_middle/src/dep_graph/mod.rs | 60 +- compiler/rustc_middle/src/hir/map/blocks.rs | 20 +- .../rustc_middle/src/hir/map/collector.rs | 23 +- compiler/rustc_middle/src/hir/map/mod.rs | 169 +- compiler/rustc_middle/src/hir/mod.rs | 8 +- compiler/rustc_middle/src/hir/place.rs | 5 +- compiler/rustc_middle/src/ich/hcx.rs | 17 +- compiler/rustc_middle/src/infer/unify_key.rs | 53 +- compiler/rustc_middle/src/lint.rs | 17 +- .../src/middle/codegen_fn_attrs.rs | 2 +- compiler/rustc_middle/src/middle/cstore.rs | 2 +- compiler/rustc_middle/src/middle/mod.rs | 2 +- .../src/middle/resolve_lifetime.rs | 2 +- compiler/rustc_middle/src/middle/stability.rs | 4 +- compiler/rustc_middle/src/mir/coverage.rs | 10 +- .../src/mir/graph_cyclic_cache.rs | 62 + .../rustc_middle/src/mir/interpret/error.rs | 3 + .../rustc_middle/src/mir/interpret/mod.rs | 9 - .../rustc_middle/src/mir/interpret/pointer.rs | 4 +- .../rustc_middle/src/mir/interpret/value.rs | 4 +- compiler/rustc_middle/src/mir/mod.rs | 43 +- compiler/rustc_middle/src/mir/mono.rs | 5 +- compiler/rustc_middle/src/mir/query.rs | 21 +- compiler/rustc_middle/src/mir/visit.rs | 4 +- compiler/rustc_middle/src/query/mod.rs | 2731 ++- compiler/rustc_middle/src/traits/chalk.rs | 40 +- compiler/rustc_middle/src/traits/mod.rs | 3 +- .../src/traits/specialization_graph.rs | 4 +- compiler/rustc_middle/src/ty/cast.rs | 14 +- compiler/rustc_middle/src/ty/codec.rs | 77 +- compiler/rustc_middle/src/ty/consts.rs | 3 +- compiler/rustc_middle/src/ty/consts/kind.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 141 +- compiler/rustc_middle/src/ty/diagnostics.rs | 25 +- compiler/rustc_middle/src/ty/error.rs | 20 +- compiler/rustc_middle/src/ty/fast_reject.rs | 7 +- compiler/rustc_middle/src/ty/flags.rs | 39 +- .../src/ty/inhabitedness/def_id_forest.rs | 120 +- .../rustc_middle/src/ty/inhabitedness/mod.rs | 61 +- compiler/rustc_middle/src/ty/instance.rs | 2 +- compiler/rustc_middle/src/ty/layout.rs | 210 +- compiler/rustc_middle/src/ty/mod.rs | 453 +- compiler/rustc_middle/src/ty/print/mod.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 81 +- compiler/rustc_middle/src/ty/query/keys.rs | 11 + compiler/rustc_middle/src/ty/query/mod.rs | 150 +- .../src/ty/query/on_disk_cache.rs | 152 +- .../rustc_middle/src/ty/structural_impls.rs | 144 +- compiler/rustc_middle/src/ty/sty.rs | 79 +- compiler/rustc_middle/src/ty/trait_def.rs | 2 +- compiler/rustc_middle/src/ty/util.rs | 14 +- compiler/rustc_middle/src/util/bug.rs | 5 +- .../rustc_mir/src/borrow_check/borrow_set.rs | 2 +- .../diagnostics/conflict_errors.rs | 222 +- .../diagnostics/explain_borrow.rs | 6 +- .../src/borrow_check/diagnostics/mod.rs | 17 +- .../borrow_check/diagnostics/move_errors.rs | 6 +- .../diagnostics/mutability_errors.rs | 88 +- .../borrow_check/diagnostics/region_errors.rs | 12 +- .../src/borrow_check/diagnostics/var_name.rs | 7 +- compiler/rustc_mir/src/borrow_check/mod.rs | 148 +- compiler/rustc_mir/src/borrow_check/nll.rs | 2 +- .../rustc_mir/src/borrow_check/path_utils.rs | 18 +- .../rustc_mir/src/borrow_check/prefixes.rs | 26 +- .../src/borrow_check/region_infer/dump_mir.rs | 4 +- .../src/borrow_check/region_infer/mod.rs | 68 +- .../rustc_mir/src/borrow_check/renumber.rs | 12 +- .../borrow_check/type_check/input_output.rs | 28 +- .../type_check/liveness/local_use_map.rs | 6 +- .../src/borrow_check/type_check/mod.rs | 39 +- .../src/borrow_check/type_check/relate_tys.rs | 6 +- .../src/borrow_check/universal_regions.rs | 22 +- .../rustc_mir/src/const_eval/eval_queries.rs | 102 +- .../rustc_mir/src/const_eval/fn_queries.rs | 2 +- compiler/rustc_mir/src/const_eval/machine.rs | 18 + .../src/dataflow/move_paths/builder.rs | 10 +- compiler/rustc_mir/src/interpret/cast.rs | 12 +- .../rustc_mir/src/interpret/eval_context.rs | 13 +- .../rustc_mir/src/interpret/intrinsics.rs | 33 +- compiler/rustc_mir/src/interpret/machine.rs | 14 + compiler/rustc_mir/src/interpret/operand.rs | 4 + compiler/rustc_mir/src/interpret/operator.rs | 3 +- compiler/rustc_mir/src/interpret/place.rs | 1 + compiler/rustc_mir/src/interpret/step.rs | 12 +- .../rustc_mir/src/interpret/terminator.rs | 11 +- compiler/rustc_mir/src/interpret/util.rs | 3 +- compiler/rustc_mir/src/interpret/validity.rs | 10 +- compiler/rustc_mir/src/lib.rs | 2 + .../rustc_mir/src/monomorphize/collector.rs | 8 +- .../src/monomorphize/partitioning/mod.rs | 3 +- .../src/monomorphize/polymorphize.rs | 23 +- compiler/rustc_mir/src/shim.rs | 4 +- .../src/transform/check_consts/ops.rs | 126 +- .../src/transform/check_consts/qualifs.rs | 24 +- .../src/transform/check_consts/validation.rs | 166 +- .../rustc_mir/src/transform/check_unsafety.rs | 33 +- .../rustc_mir/src/transform/const_prop.rs | 21 +- .../src/transform/coverage/counters.rs | 8 +- .../rustc_mir/src/transform/coverage/debug.rs | 10 +- .../rustc_mir/src/transform/coverage/graph.rs | 2 +- .../rustc_mir/src/transform/coverage/query.rs | 19 +- .../src/transform/function_item_references.rs | 12 +- compiler/rustc_mir/src/transform/inline.rs | 115 +- .../rustc_mir/src/transform/inline/cycle.rs | 157 + .../rustc_mir/src/transform/instcombine.rs | 347 +- compiler/rustc_mir/src/transform/mod.rs | 142 +- .../src/transform/no_landing_pads.rs | 27 +- .../rustc_mir/src/transform/promote_consts.rs | 436 +- .../src/transform/remove_unneeded_drops.rs | 61 +- .../rustc_mir/src/transform/rustc_peek.rs | 2 +- compiler/rustc_mir/src/transform/simplify.rs | 15 +- .../rustc_mir/src/transform/simplify_try.rs | 4 +- compiler/rustc_mir/src/util/alignment.rs | 7 +- compiler/rustc_mir/src/util/pretty.rs | 27 +- compiler/rustc_mir/src/util/storage.rs | 31 +- .../src/build/expr/as_place.rs | 95 +- .../src/build/expr/as_rvalue.rs | 57 +- .../src/build/expr/category.rs | 1 + .../rustc_mir_build/src/build/expr/into.rs | 161 +- .../rustc_mir_build/src/build/matches/mod.rs | 141 +- .../src/build/matches/simplify.rs | 7 +- .../rustc_mir_build/src/build/matches/test.rs | 2 +- .../rustc_mir_build/src/build/matches/util.rs | 4 +- compiler/rustc_mir_build/src/build/mod.rs | 20 +- compiler/rustc_mir_build/src/build/scope.rs | 11 +- compiler/rustc_mir_build/src/thir/constant.rs | 8 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 12 +- compiler/rustc_mir_build/src/thir/mod.rs | 5 + .../src/thir/pattern/check_match.rs | 7 +- .../src/thir/pattern/deconstruct_pat.rs | 15 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 8 +- .../src/thir/pattern/usefulness.rs | 2 +- compiler/rustc_parse/src/lexer/mod.rs | 25 +- .../src/lexer/unescape_error_reporting.rs | 177 +- compiler/rustc_parse/src/lib.rs | 396 +- compiler/rustc_parse/src/parser/attr.rs | 16 +- .../rustc_parse/src/parser/diagnostics.rs | 27 +- compiler/rustc_parse/src/parser/expr.rs | 92 +- compiler/rustc_parse/src/parser/generics.rs | 16 +- compiler/rustc_parse/src/parser/item.rs | 166 +- compiler/rustc_parse/src/parser/mod.rs | 101 +- .../rustc_parse/src/parser/nonterminal.rs | 145 +- compiler/rustc_parse/src/parser/pat.rs | 9 +- compiler/rustc_parse/src/parser/path.rs | 104 +- compiler/rustc_parse/src/parser/stmt.rs | 195 +- compiler/rustc_parse/src/validate_attr.rs | 5 +- compiler/rustc_parse_format/src/lib.rs | 6 +- compiler/rustc_passes/src/check_attr.rs | 325 +- compiler/rustc_passes/src/check_const.rs | 6 +- compiler/rustc_passes/src/dead.rs | 42 +- compiler/rustc_passes/src/intrinsicck.rs | 6 +- compiler/rustc_passes/src/liveness.rs | 35 +- compiler/rustc_passes/src/loops.rs | 94 +- compiler/rustc_passes/src/naked_functions.rs | 3 +- compiler/rustc_passes/src/reachable.rs | 5 +- compiler/rustc_passes/src/region.rs | 13 +- compiler/rustc_passes/src/stability.rs | 82 +- compiler/rustc_passes/src/weak_lang_items.rs | 2 +- compiler/rustc_privacy/Cargo.toml | 1 + compiler/rustc_privacy/src/lib.rs | 108 +- .../src/dep_graph/dep_node.rs | 6 - .../rustc_query_system/src/dep_graph/graph.rs | 337 +- .../rustc_query_system/src/dep_graph/mod.rs | 2 +- .../rustc_query_system/src/dep_graph/prev.rs | 2 +- .../src/dep_graph/serialized.rs | 79 +- .../rustc_query_system/src/query/caches.rs | 9 +- .../rustc_query_system/src/query/plumbing.rs | 6 +- .../rustc_resolve/src/build_reduced_graph.rs | 75 +- compiler/rustc_resolve/src/def_collector.rs | 7 +- compiler/rustc_resolve/src/diagnostics.rs | 32 +- compiler/rustc_resolve/src/late.rs | 252 +- .../rustc_resolve/src/late/diagnostics.rs | 70 +- compiler/rustc_resolve/src/late/lifetimes.rs | 102 +- compiler/rustc_resolve/src/lib.rs | 346 +- compiler/rustc_resolve/src/macros.rs | 41 +- .../rustc_save_analysis/src/dump_visitor.rs | 30 +- compiler/rustc_save_analysis/src/lib.rs | 6 +- compiler/rustc_save_analysis/src/sig.rs | 9 +- .../rustc_serialize/src/collection_impls.rs | 42 +- compiler/rustc_serialize/src/leb128.rs | 156 +- compiler/rustc_serialize/src/lib.rs | 8 +- compiler/rustc_serialize/src/opaque.rs | 488 +- compiler/rustc_serialize/src/serialize.rs | 13 +- compiler/rustc_serialize/tests/leb128.rs | 85 +- compiler/rustc_session/src/config.rs | 94 +- compiler/rustc_session/src/filesearch.rs | 54 +- compiler/rustc_session/src/options.rs | 57 +- compiler/rustc_session/src/parse.rs | 3 + compiler/rustc_session/src/session.rs | 38 +- compiler/rustc_session/src/utils.rs | 23 + .../src/analyze_source_file/tests.rs | 2 +- .../rustc_span/src/caching_source_map_view.rs | 245 +- compiler/rustc_span/src/edition.rs | 33 +- compiler/rustc_span/src/hygiene.rs | 209 +- compiler/rustc_span/src/lev_distance.rs | 16 +- compiler/rustc_span/src/lev_distance/tests.rs | 2 +- compiler/rustc_span/src/lib.rs | 126 +- compiler/rustc_span/src/source_map.rs | 63 +- compiler/rustc_span/src/source_map/tests.rs | 2 +- compiler/rustc_span/src/span_encoding.rs | 8 +- compiler/rustc_span/src/symbol.rs | 41 +- compiler/rustc_symbol_mangling/src/legacy.rs | 10 +- compiler/rustc_symbol_mangling/src/v0.rs | 5 +- compiler/rustc_target/src/abi/call/aarch64.rs | 24 +- compiler/rustc_target/src/abi/call/arm.rs | 9 +- compiler/rustc_target/src/abi/call/mod.rs | 67 +- compiler/rustc_target/src/abi/mod.rs | 97 +- .../src/spec/aarch64_be_unknown_linux_gnu.rs | 20 + .../aarch64_be_unknown_linux_gnu_ilp32.rs | 20 + .../spec/aarch64_unknown_linux_gnu_ilp32.rs | 18 + compiler/rustc_target/src/spec/abi.rs | 2 + compiler/rustc_target/src/spec/apple_base.rs | 6 +- .../src/spec/armebv7r_none_eabi.rs | 3 +- .../src/spec/armebv7r_none_eabihf.rs | 3 +- compiler/rustc_target/src/spec/crt_objects.rs | 20 +- .../rustc_target/src/spec/i386_apple_ios.rs | 9 +- .../src/spec/i386_unknown_linux_gnu.rs | 8 + .../src/spec/i486_unknown_linux_gnu.rs | 8 + .../src/spec/i686_apple_darwin.rs | 5 +- .../src/spec/i686_linux_android.rs | 5 +- .../src/spec/i686_unknown_freebsd.rs | 6 +- .../src/spec/i686_unknown_haiku.rs | 5 +- .../src/spec/i686_unknown_linux_gnu.rs | 5 +- .../src/spec/i686_unknown_linux_musl.rs | 5 +- .../src/spec/i686_unknown_netbsd.rs | 5 +- .../src/spec/i686_unknown_openbsd.rs | 5 +- .../rustc_target/src/spec/i686_wrs_vxworks.rs | 5 +- .../src/spec/linux_kernel_base.rs | 7 +- .../src/spec/mips64_unknown_linux_gnuabi64.rs | 3 +- .../spec/mips64_unknown_linux_muslabi64.rs | 3 +- .../src/spec/mips_unknown_linux_gnu.rs | 3 +- .../src/spec/mips_unknown_linux_musl.rs | 3 +- .../src/spec/mips_unknown_linux_uclibc.rs | 3 +- .../src/spec/mipsisa32r6_unknown_linux_gnu.rs | 3 +- .../mipsisa64r6_unknown_linux_gnuabi64.rs | 3 +- compiler/rustc_target/src/spec/mod.rs | 201 +- compiler/rustc_target/src/spec/msvc_base.rs | 6 +- .../src/spec/powerpc64_unknown_freebsd.rs | 3 +- .../src/spec/powerpc64_unknown_linux_gnu.rs | 3 +- .../src/spec/powerpc64_unknown_linux_musl.rs | 3 +- .../src/spec/powerpc64_wrs_vxworks.rs | 3 +- .../src/spec/powerpc_unknown_linux_gnu.rs | 3 +- .../src/spec/powerpc_unknown_linux_gnuspe.rs | 3 +- .../src/spec/powerpc_unknown_linux_musl.rs | 3 +- .../src/spec/powerpc_unknown_netbsd.rs | 7 +- .../src/spec/powerpc_wrs_vxworks.rs | 7 +- .../src/spec/powerpc_wrs_vxworks_spe.rs | 3 +- .../src/spec/s390x_unknown_linux_gnu.rs | 3 +- .../src/spec/sparc64_unknown_linux_gnu.rs | 3 +- .../src/spec/sparc64_unknown_netbsd.rs | 7 +- .../src/spec/sparc64_unknown_openbsd.rs | 3 +- .../src/spec/sparc_unknown_linux_gnu.rs | 3 +- .../src/spec/sparcv9_sun_solaris.rs | 3 +- .../rustc_target/src/spec/uefi_msvc_base.rs | 6 +- compiler/rustc_target/src/spec/wasm32_base.rs | 9 - .../src/spec/wasm32_unknown_emscripten.rs | 13 +- .../src/spec/wasm32_unknown_unknown.rs | 17 +- .../src/spec/x86_64_apple_darwin.rs | 5 +- .../rustc_target/src/spec/x86_64_apple_ios.rs | 9 +- .../src/spec/x86_64_apple_ios_macabi.rs | 9 +- .../src/spec/x86_64_apple_tvos.rs | 9 +- .../rustc_target/src/spec/x86_64_fuchsia.rs | 5 +- .../src/spec/x86_64_linux_android.rs | 5 +- .../src/spec/x86_64_rumprun_netbsd.rs | 5 +- .../src/spec/x86_64_sun_solaris.rs | 5 +- .../src/spec/x86_64_unknown_dragonfly.rs | 5 +- .../src/spec/x86_64_unknown_freebsd.rs | 5 +- .../src/spec/x86_64_unknown_haiku.rs | 5 +- .../src/spec/x86_64_unknown_hermit.rs | 5 +- .../src/spec/x86_64_unknown_hermit_kernel.rs | 5 +- .../src/spec/x86_64_unknown_linux_gnu.rs | 5 +- .../src/spec/x86_64_unknown_linux_gnux32.rs | 5 +- .../src/spec/x86_64_unknown_linux_musl.rs | 6 +- .../src/spec/x86_64_unknown_netbsd.rs | 5 +- .../src/spec/x86_64_unknown_openbsd.rs | 5 +- .../src/spec/x86_64_unknown_redox.rs | 5 +- .../src/spec/x86_64_wrs_vxworks.rs | 5 +- compiler/rustc_trait_selection/src/infer.rs | 2 +- compiler/rustc_trait_selection/src/lib.rs | 1 - .../rustc_trait_selection/src/opaque_types.rs | 26 +- .../src/traits/auto_trait.rs | 48 +- .../src/traits/coherence.rs | 6 +- .../src/traits/const_evaluatable.rs | 93 +- .../src/traits/error_reporting/mod.rs | 110 +- .../src/traits/error_reporting/suggestions.rs | 70 +- .../src/traits/fulfill.rs | 51 +- .../rustc_trait_selection/src/traits/mod.rs | 7 +- .../src/traits/object_safety.rs | 58 +- .../src/traits/project.rs | 8 +- .../traits/query/type_op/prove_predicate.rs | 2 +- .../src/traits/select/candidate_assembly.rs | 58 +- .../src/traits/select/confirmation.rs | 66 +- .../src/traits/select/mod.rs | 40 +- .../rustc_trait_selection/src/traits/wf.rs | 50 +- compiler/rustc_traits/Cargo.toml | 7 +- compiler/rustc_traits/src/chalk/db.rs | 116 +- compiler/rustc_traits/src/chalk/lowering.rs | 186 +- compiler/rustc_traits/src/chalk/mod.rs | 40 +- .../src/implied_outlives_bounds.rs | 28 +- .../src/normalize_erasing_regions.rs | 22 +- compiler/rustc_traits/src/type_op.rs | 4 +- compiler/rustc_ty_utils/src/ty.rs | 29 +- compiler/rustc_type_ir/Cargo.toml | 1 + compiler/rustc_type_ir/src/lib.rs | 406 + compiler/rustc_typeck/src/astconv/errors.rs | 4 +- compiler/rustc_typeck/src/astconv/generics.rs | 342 +- compiler/rustc_typeck/src/astconv/mod.rs | 242 +- compiler/rustc_typeck/src/check/_match.rs | 214 +- compiler/rustc_typeck/src/check/callee.rs | 24 +- compiler/rustc_typeck/src/check/cast.rs | 18 +- compiler/rustc_typeck/src/check/check.rs | 287 +- compiler/rustc_typeck/src/check/closure.rs | 10 +- compiler/rustc_typeck/src/check/coercion.rs | 36 +- .../rustc_typeck/src/check/compare_method.rs | 13 +- compiler/rustc_typeck/src/check/demand.rs | 8 + compiler/rustc_typeck/src/check/dropck.rs | 12 +- compiler/rustc_typeck/src/check/expr.rs | 289 +- .../rustc_typeck/src/check/fn_ctxt/_impl.rs | 63 +- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 121 +- .../rustc_typeck/src/check/fn_ctxt/mod.rs | 12 +- .../src/check/fn_ctxt/suggestions.rs | 35 +- compiler/rustc_typeck/src/check/intrinsic.rs | 4 +- .../rustc_typeck/src/check/method/confirm.rs | 16 +- compiler/rustc_typeck/src/check/method/mod.rs | 9 +- .../rustc_typeck/src/check/method/probe.rs | 82 +- .../rustc_typeck/src/check/method/suggest.rs | 79 +- compiler/rustc_typeck/src/check/mod.rs | 18 +- compiler/rustc_typeck/src/check/pat.rs | 86 +- compiler/rustc_typeck/src/check/regionck.rs | 9 +- compiler/rustc_typeck/src/check/upvar.rs | 512 +- compiler/rustc_typeck/src/check/wfcheck.rs | 51 +- compiler/rustc_typeck/src/check/writeback.rs | 18 +- .../rustc_typeck/src/coherence/builtin.rs | 10 +- .../src/coherence/inherent_impls.rs | 33 +- compiler/rustc_typeck/src/coherence/orphan.rs | 5 +- .../rustc_typeck/src/coherence/unsafety.rs | 9 +- compiler/rustc_typeck/src/collect.rs | 187 +- .../rustc_typeck/src/collect/item_bounds.rs | 13 +- compiler/rustc_typeck/src/collect/type_of.rs | 57 +- .../src/constrained_generic_params.rs | 3 +- compiler/rustc_typeck/src/expr_use_visitor.rs | 20 +- compiler/rustc_typeck/src/impl_wf_check.rs | 6 +- .../src/impl_wf_check/min_specialization.rs | 32 +- compiler/rustc_typeck/src/lib.rs | 8 +- .../rustc_typeck/src/mem_categorization.rs | 7 +- .../rustc_typeck/src/outlives/explicit.rs | 24 +- .../src/outlives/implicit_infer.rs | 2 +- compiler/rustc_typeck/src/outlives/mod.rs | 14 +- .../rustc_typeck/src/structured_errors.rs | 147 +- .../missing_cast_for_variadic_arg.rs | 58 + .../structured_errors/sized_unsized_cast.rs | 57 + .../wrong_number_of_generic_args.rs | 393 + .../rustc_typeck/src/variance/constraints.rs | 19 +- config.toml.example | 12 +- git-commit-hash | 2 +- library/alloc/Cargo.toml | 4 +- library/alloc/src/alloc.rs | 23 + library/alloc/src/borrow.rs | 5 + library/alloc/src/boxed.rs | 230 +- library/alloc/src/collections/binary_heap.rs | 19 +- library/alloc/src/collections/btree/append.rs | 11 +- library/alloc/src/collections/btree/map.rs | 125 +- .../alloc/src/collections/btree/map/tests.rs | 176 +- library/alloc/src/collections/btree/mod.rs | 7 +- .../alloc/src/collections/btree/navigate.rs | 53 +- library/alloc/src/collections/btree/node.rs | 657 +- .../alloc/src/collections/btree/node/tests.rs | 22 +- library/alloc/src/collections/btree/remove.rs | 22 +- library/alloc/src/collections/btree/search.rs | 132 +- library/alloc/src/collections/btree/set.rs | 106 +- .../alloc/src/collections/btree/set/tests.rs | 24 +- library/alloc/src/collections/btree/split.rs | 13 +- library/alloc/src/collections/linked_list.rs | 1 + .../alloc/src/collections/vec_deque/mod.rs | 38 +- .../alloc/src/collections/vec_deque/tests.rs | 55 + library/alloc/src/fmt.rs | 9 +- library/alloc/src/lib.rs | 19 +- library/alloc/src/macros.rs | 20 +- library/alloc/src/raw_vec.rs | 31 +- library/alloc/src/rc.rs | 254 +- library/alloc/src/rc/tests.rs | 41 + library/alloc/src/slice.rs | 2 + library/alloc/src/str.rs | 2 - library/alloc/src/string.rs | 21 +- library/alloc/src/sync.rs | 254 +- library/alloc/src/sync/tests.rs | 43 +- library/alloc/src/task.rs | 76 +- library/alloc/src/vec/cow.rs | 35 + library/alloc/src/vec/drain.rs | 155 + library/alloc/src/vec/drain_filter.rs | 143 + library/alloc/src/vec/in_place_drop.rs | 24 + library/alloc/src/vec/into_iter.rs | 283 + library/alloc/src/vec/is_zero.rs | 71 + library/alloc/src/{vec.rs => vec/mod.rs} | 1481 +- library/alloc/src/vec/partial_eq.rs | 43 + library/alloc/src/vec/set_len_on_drop.rs | 28 + library/alloc/src/vec/source_iter_marker.rs | 108 + library/alloc/src/vec/spec_extend.rs | 82 + library/alloc/src/vec/spec_from_elem.rs | 60 + library/alloc/src/vec/spec_from_iter.rs | 97 + .../alloc/src/vec/spec_from_iter_nested.rs | 56 + library/alloc/src/vec/splice.rs | 133 + library/alloc/tests/lib.rs | 4 +- library/alloc/tests/slice.rs | 58 + library/alloc/tests/string.rs | 50 + library/alloc/tests/vec.rs | 70 + library/core/benches/iter.rs | 24 +- library/core/src/alloc/global.rs | 21 + library/core/src/alloc/layout.rs | 6 + library/core/src/alloc/mod.rs | 5 +- library/core/src/any.rs | 23 + library/core/src/array/iter.rs | 25 +- library/core/src/array/mod.rs | 77 +- library/core/src/borrow.rs | 3 - library/core/src/cell.rs | 130 +- library/core/src/char/convert.rs | 43 + library/core/src/cmp.rs | 20 +- library/core/src/convert/mod.rs | 21 +- library/core/src/default.rs | 6 +- library/core/src/fmt/mod.rs | 38 +- library/core/src/fmt/num.rs | 55 +- library/core/src/hint.rs | 2 +- library/core/src/intrinsics.rs | 50 +- library/core/src/iter/adapters/chain.rs | 2 +- library/core/src/iter/adapters/flatten.rs | 16 +- library/core/src/iter/adapters/fuse.rs | 15 +- library/core/src/iter/adapters/intersperse.rs | 187 + library/core/src/iter/adapters/mod.rs | 4 + library/core/src/iter/adapters/peekable.rs | 7 +- library/core/src/iter/adapters/skip.rs | 10 +- library/core/src/iter/adapters/zip.rs | 3 +- library/core/src/iter/mod.rs | 2 + library/core/src/iter/range.rs | 4 +- library/core/src/iter/traits/exact_size.rs | 1 + library/core/src/iter/traits/iterator.rs | 143 +- library/core/src/iter/traits/marker.rs | 10 +- library/core/src/lib.rs | 13 +- library/core/src/macros/mod.rs | 32 +- library/core/src/macros/panic.md | 15 +- library/core/src/mem/maybe_uninit.rs | 82 +- library/core/src/mem/mod.rs | 31 +- library/core/src/num/dec2flt/mod.rs | 4 +- library/core/src/num/f32.rs | 64 +- library/core/src/num/f64.rs | 64 +- library/core/src/num/int_macros.rs | 4202 ++-- library/core/src/num/mod.rs | 39 +- library/core/src/num/nonzero.rs | 219 +- library/core/src/num/shells/i128.rs | 9 +- library/core/src/num/shells/i16.rs | 9 +- library/core/src/num/shells/i32.rs | 9 +- library/core/src/num/shells/i64.rs | 9 +- library/core/src/num/shells/i8.rs | 9 +- library/core/src/num/shells/int_macros.rs | 81 +- library/core/src/num/shells/isize.rs | 9 +- library/core/src/num/shells/u128.rs | 10 +- library/core/src/num/shells/u16.rs | 9 +- library/core/src/num/shells/u32.rs | 9 +- library/core/src/num/shells/u64.rs | 9 +- library/core/src/num/shells/u8.rs | 9 +- library/core/src/num/shells/usize.rs | 9 +- library/core/src/num/uint_macros.rs | 3274 ++- library/core/src/num/wrapping.rs | 767 +- library/core/src/ops/range.rs | 23 + library/core/src/option.rs | 36 +- library/core/src/panic.rs | 34 + library/core/src/pin.rs | 2 - library/core/src/ptr/const_ptr.rs | 100 +- library/core/src/ptr/mod.rs | 44 +- library/core/src/ptr/mut_ptr.rs | 100 +- library/core/src/ptr/non_null.rs | 19 +- library/core/src/result.rs | 76 +- library/core/src/slice/cmp.rs | 27 - library/core/src/slice/iter.rs | 208 +- library/core/src/slice/mod.rs | 339 +- library/core/src/slice/raw.rs | 2 +- library/core/src/str/iter.rs | 20 +- library/core/src/str/mod.rs | 11 +- library/core/src/stream/mod.rs | 127 + library/core/src/stream/stream/mod.rs | 110 + library/core/src/sync/atomic.rs | 1455 +- library/core/src/task/poll.rs | 4 +- library/core/src/time.rs | 20 +- library/core/tests/const_ptr.rs | 51 + library/core/tests/iter.rs | 3507 --- library/core/tests/iter/adapters/chain.rs | 272 + library/core/tests/iter/adapters/cloned.rs | 52 + library/core/tests/iter/adapters/copied.rs | 18 + library/core/tests/iter/adapters/cycle.rs | 31 + library/core/tests/iter/adapters/enumerate.rs | 107 + library/core/tests/iter/adapters/filter.rs | 52 + .../core/tests/iter/adapters/filter_map.rs | 50 + library/core/tests/iter/adapters/flat_map.rs | 74 + library/core/tests/iter/adapters/flatten.rs | 111 + library/core/tests/iter/adapters/fuse.rs | 75 + library/core/tests/iter/adapters/inspect.rs | 38 + .../core/tests/iter/adapters/intersperse.rs | 154 + library/core/tests/iter/adapters/map.rs | 27 + library/core/tests/iter/adapters/mod.rs | 185 + library/core/tests/iter/adapters/peekable.rs | 272 + library/core/tests/iter/adapters/scan.rs | 20 + library/core/tests/iter/adapters/skip.rs | 181 + .../core/tests/iter/adapters/skip_while.rs | 50 + library/core/tests/iter/adapters/step_by.rs | 248 + library/core/tests/iter/adapters/take.rs | 126 + .../core/tests/iter/adapters/take_while.rs | 29 + library/core/tests/iter/adapters/zip.rs | 247 + library/core/tests/iter/mod.rs | 102 + library/core/tests/iter/range.rs | 446 + library/core/tests/iter/sources.rs | 108 + library/core/tests/iter/traits/accum.rs | 66 + .../core/tests/iter/traits/double_ended.rs | 90 + library/core/tests/iter/traits/iterator.rs | 470 + library/core/tests/iter/traits/mod.rs | 4 + library/core/tests/iter/traits/step.rs | 89 + library/core/tests/lib.rs | 15 +- library/core/tests/mem.rs | 24 +- library/core/tests/nonzero.rs | 16 + library/core/tests/num/dec2flt/mod.rs | 2 - library/core/tests/num/dec2flt/rawfp.rs | 3 - .../core/tests/num/flt2dec/strategy/dragon.rs | 1 - .../core/tests/num/flt2dec/strategy/grisu.rs | 1 - library/core/tests/num/mod.rs | 2 + library/core/tests/num/ops.rs | 244 + library/core/tests/num/wrapping.rs | 29 +- library/core/tests/option.rs | 7 + library/core/tests/result.rs | 12 + library/panic_abort/src/lib.rs | 4 +- library/panic_unwind/src/lib.rs | 7 +- library/proc_macro/src/lib.rs | 3 +- library/rtstartup/rsbegin.rs | 3 +- library/rtstartup/rsend.rs | 3 +- library/std/Cargo.toml | 4 +- library/std/src/alloc.rs | 5 +- library/std/src/backtrace.rs | 65 +- library/std/src/backtrace/tests.rs | 114 +- library/std/src/collections/hash/map.rs | 28 +- library/std/src/collections/hash/set.rs | 28 +- library/std/src/collections/mod.rs | 8 +- library/std/src/env.rs | 15 +- library/std/src/error.rs | 23 +- library/std/src/f32.rs | 23 +- library/std/src/f64.rs | 23 +- library/std/src/ffi/c_str.rs | 14 +- library/std/src/ffi/os_str.rs | 40 +- library/std/src/fs.rs | 3 + library/std/src/future.rs | 17 - library/std/src/io/buffered/bufreader.rs | 15 +- library/std/src/io/buffered/bufwriter.rs | 113 +- library/std/src/io/buffered/tests.rs | 12 + library/std/src/io/copy.rs | 80 +- library/std/src/io/mod.rs | 95 +- library/std/src/io/prelude.rs | 2 +- library/std/src/io/util.rs | 17 +- library/std/src/io/util/tests.rs | 70 +- library/std/src/keyword_docs.rs | 2 +- library/std/src/lib.rs | 36 +- library/std/src/macros.rs | 22 +- library/std/src/net/ip.rs | 91 +- library/std/src/net/ip/tests.rs | 3 + library/std/src/os/linux/raw.rs | 16 +- library/std/src/panic.rs | 39 +- library/std/src/panicking.rs | 10 +- library/std/src/path.rs | 70 +- library/std/src/prelude/mod.rs | 28 +- library/std/src/prelude/v1.rs | 8 +- library/std/src/primitive_docs.rs | 16 +- library/std/src/sync/once.rs | 20 +- library/std/src/sync/once/tests.rs | 4 +- library/std/src/sys/hermit/mutex.rs | 5 +- .../std/src/sys/sgx/waitqueue/spin_mutex.rs | 5 +- library/std/src/sys/unix/ext/process.rs | 74 +- library/std/src/sys/unix/kernel_copy.rs | 18 +- library/std/src/sys/unix/kernel_copy/tests.rs | 18 + library/std/src/sys/unix/net.rs | 2 +- .../src/sys/unix/process/process_common.rs | 9 + .../src/sys/unix/process/process_fuchsia.rs | 44 + .../std/src/sys/unix/process/process_unix.rs | 57 +- library/std/src/sys/unix/process/zircon.rs | 1 - library/std/src/sys/unix/time.rs | 2 +- library/std/src/sys/unix/weak.rs | 2 +- library/std/src/sys/wasi/ext/fs.rs | 8 + library/std/src/sys/wasi/fs.rs | 53 +- library/std/src/sys/wasi/os.rs | 45 +- library/std/src/sys/wasm/thread.rs | 2 +- library/std/src/sys/windows/c.rs | 117 +- library/std/src/sys/windows/compat.rs | 151 +- library/std/src/sys/windows/ext/process.rs | 16 +- library/std/src/sys/windows/mutex.rs | 110 +- library/std/src/sys/windows/thread_parker.rs | 12 +- library/std/src/sys_common/fs.rs | 10 +- library/std/src/thread/mod.rs | 19 +- library/term/src/terminfo/parm/tests.rs | 12 +- library/test/Cargo.toml | 2 +- library/test/src/cli.rs | 2 +- library/test/src/lib.rs | 89 +- library/test/src/test_result.rs | 2 +- library/test/src/tests.rs | 48 +- library/unwind/build.rs | 4 + library/unwind/src/libunwind.rs | 5 +- src/bootstrap/CHANGELOG.md | 7 +- src/bootstrap/README.md | 1 - src/bootstrap/bin/main.rs | 2 +- src/bootstrap/bootstrap.py | 67 +- src/bootstrap/bootstrap_test.py | 11 +- src/bootstrap/builder.rs | 38 +- src/bootstrap/builder/tests.rs | 6 +- src/bootstrap/channel.rs | 4 +- src/bootstrap/check.rs | 69 +- src/bootstrap/clean.rs | 1 + src/bootstrap/compile.rs | 76 +- src/bootstrap/config.rs | 14 +- src/bootstrap/configure.py | 4 + src/bootstrap/dist.rs | 56 +- src/bootstrap/doc.rs | 32 +- src/bootstrap/download-ci-llvm-stamp | 2 +- src/bootstrap/flags.rs | 6 +- src/bootstrap/lib.rs | 4 +- src/bootstrap/mk/Makefile.in | 4 +- src/bootstrap/native.rs | 2 +- src/bootstrap/sanity.rs | 6 +- src/bootstrap/setup.rs | 4 +- src/bootstrap/tarball.rs | 18 +- src/bootstrap/test.rs | 100 +- src/bootstrap/tool.rs | 21 +- .../docker/host-x86_64/armhf-gnu/Dockerfile | 18 +- .../host-x86_64/armhf-gnu/vexpress_config | 28 +- .../host-x86_64/dist-various-1/Dockerfile | 10 +- .../dist-various-1/build-rumprun.sh | 5 +- .../host-x86_64/dist-various-2/Dockerfile | 21 +- .../dist-various-2/build-solaris-toolchain.sh | 2 +- .../dist-various-2/build-wasi-toolchain.sh | 10 +- ...d-x86_64-fortanix-unknown-sgx-toolchain.sh | 2 +- .../host-x86_64/test-various/Dockerfile | 5 +- src/ci/docker/scripts/cross-apt-packages.sh | 2 +- src/ci/pgo.sh | 14 + src/ci/run.sh | 5 + src/ci/scripts/install-awscli.sh | 2 +- src/doc/book/.github/workflows/main.yml | 8 +- src/doc/book/ADMIN_TASKS.md | 123 +- .../output.txt | 15 - .../src/main.rs | 21 +- .../no-listing-12-if-let/src/main.rs | 2 +- .../listing-11-10/output.txt | 2 +- .../ch12-an-io-project/listing-12-03/poem.txt | 6 +- .../listing-12-04/output.txt | 6 +- .../ch12-an-io-project/listing-12-04/poem.txt | 6 +- .../ch12-an-io-project/listing-12-05/poem.txt | 6 +- .../ch12-an-io-project/listing-12-06/poem.txt | 6 +- .../ch12-an-io-project/listing-12-07/poem.txt | 6 +- .../ch12-an-io-project/listing-12-08/poem.txt | 6 +- .../ch12-an-io-project/listing-12-09/poem.txt | 6 +- .../ch12-an-io-project/listing-12-10/poem.txt | 6 +- .../ch12-an-io-project/listing-12-11/poem.txt | 6 +- .../listing-12-12/output.txt | 6 +- .../ch12-an-io-project/listing-12-12/poem.txt | 6 +- .../ch12-an-io-project/listing-12-13/poem.txt | 6 +- .../ch12-an-io-project/listing-12-14/poem.txt | 6 +- .../ch12-an-io-project/listing-12-15/poem.txt | 6 +- .../ch12-an-io-project/listing-12-16/poem.txt | 6 +- .../ch12-an-io-project/listing-12-17/poem.txt | 6 +- .../ch12-an-io-project/listing-12-18/poem.txt | 6 +- .../ch12-an-io-project/listing-12-19/poem.txt | 6 +- .../ch12-an-io-project/listing-12-20/poem.txt | 6 +- .../ch12-an-io-project/listing-12-21/poem.txt | 6 +- .../ch12-an-io-project/listing-12-22/poem.txt | 6 +- .../ch12-an-io-project/listing-12-23/poem.txt | 6 +- .../ch12-an-io-project/listing-12-24/poem.txt | 6 +- .../poem.txt | 6 +- .../poem.txt | 6 +- .../output-only-02-missing-lifetimes/poem.txt | 6 +- .../output.txt | 2 +- .../output-only-03-multiple-matches/poem.txt | 6 +- .../output-only-04-no-matches/poem.txt | 6 +- .../output.txt | 3 +- src/doc/book/rust-toolchain | 2 +- src/doc/book/src/ch03-02-data-types.md | 27 +- src/doc/book/src/ch04-01-what-is-ownership.md | 5 +- ...referring-to-an-item-in-the-module-tree.md | 4 +- ...g-paths-into-scope-with-the-use-keyword.md | 8 +- src/doc/book/src/ch10-02-traits.md | 10 +- src/doc/book/src/ch11-02-running-tests.md | 2 +- src/doc/book/src/ch11-03-test-organization.md | 2 +- src/doc/book/src/ch13-01-closures.md | 6 +- src/doc/book/src/ch14-03-cargo-workspaces.md | 4 +- .../book/src/ch15-05-interior-mutability.md | 2 +- src/doc/book/src/ch16-03-shared-state.md | 4 +- src/doc/book/src/ch17-01-what-is-oo.md | 4 +- src/doc/book/src/ch17-02-trait-objects.md | 8 +- .../book/src/ch17-03-oo-design-patterns.md | 10 +- src/doc/book/src/ch19-03-advanced-traits.md | 6 +- src/doc/book/src/ch19-04-advanced-types.md | 2 +- ...ch19-05-advanced-functions-and-closures.md | 2 +- src/doc/book/src/ch19-06-macros.md | 4 +- src/doc/book/src/ch20-02-multithreaded.md | 4 +- src/doc/book/src/title-page.md | 2 +- src/doc/book/tools/update-rustc.sh | 17 +- src/doc/embedded-book/book.toml | 3 + src/doc/nomicon/src/SUMMARY.md | 6 + src/doc/nomicon/src/arc-and-mutex.md | 2 +- src/doc/nomicon/src/arc-base.md | 136 + src/doc/nomicon/src/arc-clone.md | 94 + src/doc/nomicon/src/arc-drop.md | 98 + src/doc/nomicon/src/arc-final.md | 82 + src/doc/nomicon/src/arc-layout.md | 70 + src/doc/nomicon/src/arc.md | 13 + src/doc/nomicon/src/exotic-sizes.md | 2 +- src/doc/nomicon/src/vec-alloc.md | 42 +- src/doc/nomicon/src/vec-dealloc.md | 9 +- src/doc/nomicon/src/vec-drain.md | 6 +- src/doc/nomicon/src/vec-final.md | 10 +- src/doc/nomicon/src/vec-insert-remove.md | 12 +- src/doc/nomicon/src/vec-into-iter.md | 19 +- src/doc/nomicon/src/vec-layout.md | 15 +- src/doc/nomicon/src/vec-push-pop.md | 4 +- src/doc/nomicon/src/vec-raw.md | 43 +- src/doc/nomicon/src/vec-zsts.md | 38 +- src/doc/reference/.github/workflows/main.yml | 2 +- src/doc/reference/src/SUMMARY.md | 12 +- src/doc/reference/src/abi.md | 3 + src/doc/reference/src/attributes.md | 11 +- .../src/behavior-not-considered-unsafe.md | 17 + src/doc/reference/src/const_eval.md | 2 + .../reference/src/crates-and-source-files.md | 42 +- src/doc/reference/src/expressions.md | 63 +- .../reference/src/expressions/array-expr.md | 47 +- .../src/expressions/operator-expr.md | 156 +- src/doc/reference/src/glossary.md | 60 + .../reference/src/items/associated-items.md | 41 +- src/doc/reference/src/items/constant-items.md | 5 +- src/doc/reference/src/items/enumerations.md | 4 +- src/doc/reference/src/items/extern-crates.md | 64 +- .../reference/src/items/external-blocks.md | 39 +- src/doc/reference/src/items/functions.md | 61 +- src/doc/reference/src/items/generics.md | 239 +- .../reference/src/items/implementations.md | 125 +- src/doc/reference/src/items/modules.md | 31 +- src/doc/reference/src/items/static-items.md | 6 +- src/doc/reference/src/items/structs.md | 6 +- src/doc/reference/src/items/traits.md | 79 +- src/doc/reference/src/items/type-aliases.md | 14 +- src/doc/reference/src/items/unions.md | 4 +- .../reference/src/items/use-declarations.md | 2 +- src/doc/reference/src/keywords.md | 6 +- src/doc/reference/src/linkage.md | 2 +- src/doc/reference/src/macros-by-example.md | 3 +- src/doc/reference/src/macros.md | 2 +- src/doc/reference/src/names.md | 143 + .../reference/src/names/name-resolution.md | 3 + src/doc/reference/src/names/namespaces.md | 158 + src/doc/reference/src/names/preludes.md | 157 + src/doc/reference/src/names/scopes.md | 3 + src/doc/reference/src/paths.md | 39 +- src/doc/reference/src/tokens.md | 6 +- src/doc/reference/src/types/enum.md | 2 +- .../reference/src/types/function-pointer.md | 7 +- src/doc/reference/src/types/struct.md | 2 +- src/doc/reference/src/types/tuple.md | 1 + src/doc/rust-by-example/.travis.yml | 2 +- .../src/flow_control/match/guard.md | 16 + src/doc/rust-by-example/src/fn/closures.md | 6 +- .../std_misc/threads/testcase_mapreduce.md | 12 +- src/doc/rustc/book.toml | 3 + src/doc/rustc/src/codegen-options/index.md | 30 +- .../src/lints/listing/deny-by-default.md | 3 + src/doc/rustc/src/platform-support.md | 3 + src/doc/rustdoc/book.toml | 3 + src/doc/rustdoc/src/command-line-arguments.md | 20 + .../rustdoc/src/how-to-write-documentation.md | 66 +- src/doc/rustdoc/src/what-is-rustdoc.md | 2 +- .../src/compiler-flags/sanitizer.md | 28 +- .../source-based-code-coverage.md | 2 +- .../abi-c-cmse-nonsecure-call.md | 88 + .../const-in-array-repeat-expressions.md | 11 - .../src/language-features/ffi-pure.md | 2 +- .../language-features/intra-doc-pointers.md | 15 + .../src/language-features/link-args.md | 2 +- .../check_missing_items.py | 2 + src/etc/generate-deriving-span-tests.py | 2 +- src/etc/htmldocck.py | 12 +- src/etc/natvis/intrinsic.natvis | 18 +- src/etc/natvis/liballoc.natvis | 46 +- src/etc/natvis/libcore.natvis | 26 +- src/etc/natvis/libstd.natvis | 8 +- src/librustdoc/Cargo.toml | 2 + src/librustdoc/clean/auto_trait.rs | 38 +- src/librustdoc/clean/blanket_impl.rs | 4 +- src/librustdoc/clean/cfg.rs | 16 +- src/librustdoc/clean/inline.rs | 182 +- src/librustdoc/clean/mod.rs | 360 +- src/librustdoc/clean/simplify.rs | 2 +- src/librustdoc/clean/types.rs | 396 +- src/librustdoc/clean/utils.rs | 85 +- src/librustdoc/config.rs | 58 +- src/librustdoc/core.rs | 106 +- src/librustdoc/doctest.rs | 42 +- src/librustdoc/doctest/tests.rs | 4 +- src/librustdoc/doctree.rs | 40 - src/librustdoc/fold.rs | 18 +- src/librustdoc/formats/cache.rs | 66 +- src/librustdoc/formats/item_type.rs | 5 +- src/librustdoc/formats/mod.rs | 7 +- src/librustdoc/formats/renderer.rs | 78 +- src/librustdoc/html/escape.rs | 31 +- src/librustdoc/html/format.rs | 556 +- src/librustdoc/html/highlight.rs | 46 +- src/librustdoc/html/highlight/tests.rs | 9 +- src/librustdoc/html/layout.rs | 36 +- src/librustdoc/html/markdown.rs | 168 +- src/librustdoc/html/mod.rs | 1 + src/librustdoc/html/render/cache.rs | 55 +- src/librustdoc/html/render/mod.rs | 1638 +- src/librustdoc/html/render/tests.rs | 11 + src/librustdoc/html/sources.rs | 14 +- src/librustdoc/html/static/main.js | 263 +- src/librustdoc/html/static/rustdoc.css | 30 +- src/librustdoc/html/static/settings.js | 2 +- src/librustdoc/html/static/source-script.js | 10 +- src/librustdoc/html/static/storage.js | 10 +- src/librustdoc/html/static/themes/dark.css | 1 - src/librustdoc/html/toc.rs | 2 +- src/librustdoc/json/conversions.rs | 129 +- src/librustdoc/json/mod.rs | 117 +- src/librustdoc/lib.rs | 35 +- .../passes/calculate_doc_coverage.rs | 20 +- .../passes/check_code_block_syntax.rs | 17 +- src/librustdoc/passes/collapse_docs.rs | 72 - .../passes/collect_intra_doc_links.rs | 604 +- src/librustdoc/passes/collect_trait_impls.rs | 144 +- src/librustdoc/passes/doc_test_lints.rs | 6 +- src/librustdoc/passes/html_tags.rs | 2 +- src/librustdoc/passes/mod.rs | 5 - src/librustdoc/passes/non_autolinks.rs | 2 +- src/librustdoc/passes/strip_hidden.rs | 10 +- src/librustdoc/passes/stripper.rs | 14 +- src/librustdoc/passes/unindent_comments.rs | 18 +- .../passes/unindent_comments/tests.rs | 14 +- src/librustdoc/theme.rs | 10 +- src/librustdoc/visit_ast.rs | 68 +- src/rustdoc-json-types/Cargo.toml | 11 + src/rustdoc-json-types/README.md | 12 + .../types.rs => rustdoc-json-types/lib.rs} | 24 +- src/stage0.txt | 6 +- src/test/codegen/abi-repr-ext.rs | 13 + src/test/codegen/call-llvm-intrinsics.rs | 2 +- src/test/codegen/dealloc-no-unwind.rs | 2 +- src/test/codegen/debug-column.rs | 2 +- src/test/codegen/debug-linkage-name.rs | 6 +- src/test/codegen/export-no-mangle.rs | 8 +- src/test/codegen/ffi-const.rs | 2 +- src/test/codegen/ffi-out-of-bounds-loads.rs | 2 +- src/test/codegen/ffi-pure.rs | 2 +- src/test/codegen/ffi-returns-twice.rs | 2 +- src/test/codegen/intrinsics/move-val-init.rs | 19 - src/test/codegen/issue-47278.rs | 2 +- src/test/codegen/issue-59352.rs | 18 + .../codegen/repr-transparent-aggregates-1.rs | 16 +- src/test/codegen/repr-transparent.rs | 34 +- .../codegen/riscv-abi/call-llvm-intrinsics.rs | 2 +- src/test/codegen/sanitizer-no-sanitize.rs | 2 +- src/test/codegen/slice-as_chunks.rs | 33 + src/test/codegen/slice-ref-equality.rs | 16 + src/test/codegen/stack-probes.rs | 1 - src/test/codegen/target-cpu-on-functions.rs | 2 +- src/test/codegen/unwind-extern-exports.rs | 2 +- src/test/codegen/unwind-extern-imports.rs | 2 +- src/test/codegen/vec-shrink-panic.rs | 8 - src/test/codegen/vecdeque_no_panic.rs | 19 + .../auxiliary/panic-runtime-lang-items.rs | 15 - .../auxiliary/panic-runtime-unwind.rs | 17 - .../auxiliary/panic-runtime-unwind2.rs | 17 - .../compile-fail/auxiliary/some-panic-impl.rs | 11 - .../auxiliary/wants-panic-runtime-unwind.rs | 6 - .../runtime-depend-on-needs-runtime.stderr | 4 - .../debuginfo/pretty-std-collections-hash.rs | 8 +- src/test/debuginfo/pretty-std.rs | 14 +- src/test/debuginfo/type-names.rs | 58 +- src/test/incremental/foreign.rs | 8 +- src/test/incremental/hashes/if_expressions.rs | 2 +- src/test/incremental/hashes/inherent_impls.rs | 2 +- src/test/incremental/hashes/trait_defs.rs | 2 +- .../incremental/issue-80336-invalid-span.rs | 10 + .../mir-opt/const-promotion-extern-static.rs | 4 +- ..._allocation.main.ConstProp.after.32bit.mir | 28 +- ..._allocation.main.ConstProp.after.64bit.mir | 32 +- ...allocation2.main.ConstProp.after.32bit.mir | 30 +- ...allocation2.main.ConstProp.after.64bit.mir | 32 +- ...allocation3.main.ConstProp.after.32bit.mir | 16 +- ...allocation3.main.ConstProp.after.64bit.mir | 14 +- ...[0].SimplifyCfg-elaborate-drops.after.mir} | 4 +- ...[0].SimplifyCfg-elaborate-drops.after.mir} | 2 +- ...l_flow_simplification.hello.ConstProp.diff | 26 +- ...age_graphviz.main.InstrumentCoverage.0.dot | 4 +- ...egator_test_enum_2.test1.Deaggregator.diff | 24 +- .../branch.main.DestinationPropagation.diff | 16 +- .../mir-opt/equal_true.opt.InstCombine.diff | 8 +- ...t_opt_bool.SimplifyComparisonIntegral.diff | 8 +- ...opt_floats.SimplifyComparisonIntegral.diff | 8 +- ...t.opt_char.SimplifyComparisonIntegral.diff | 12 +- ...int.opt_i8.SimplifyComparisonIntegral.diff | 12 +- ...ltiple_ifs.SimplifyComparisonIntegral.diff | 30 +- ...t_negative.SimplifyComparisonIntegral.diff | 12 +- ...nt.opt_u32.SimplifyComparisonIntegral.diff | 12 +- src/test/mir-opt/inline/cycle.f.Inline.diff | 42 + src/test/mir-opt/inline/cycle.g.Inline.diff | 25 + .../mir-opt/inline/cycle.main.Inline.diff | 25 + src/test/mir-opt/inline/cycle.rs | 18 + .../mir-opt/inline/inline-cycle-generic.rs | 40 + .../mir-opt/inline/inline-instruction-set.rs | 54 + ...e_closure_borrows_arg.foo.Inline.after.mir | 2 +- .../inline_cycle_generic.main.Inline.diff | 29 + .../inline/inline_diverging.g.Inline.diff | 20 +- .../inline/inline_generator.main.Inline.diff | 6 +- ...inline_instruction_set.default.Inline.diff | 45 + .../inline_instruction_set.t32.Inline.diff | 47 + ...e_deref.do_not_miscompile.InstCombine.diff | 26 +- src/test/mir-opt/inst_combine_deref.rs | 69 - ...ment_coverage.main.InstrumentCoverage.diff | 25 +- ...e_38669.main.SimplifyCfg-initial.after.mir | 21 +- .../issue_41888.main.ElaborateDrops.after.mir | 14 +- .../issue_73223.main.PreCodegen.32bit.diff | 120 +- .../issue_73223.main.PreCodegen.64bit.diff | 120 +- ..._73223.main.SimplifyArmIdentity.32bit.diff | 234 +- ..._73223.main.SimplifyArmIdentity.64bit.diff | 234 +- ...76432.test.SimplifyComparisonIntegral.diff | 8 +- src/test/mir-opt/issues/issue-59352.rs | 19 + ...ue_59352.num_to_digit.PreCodegen.after.mir | 102 + ....main.SimplifyCfg-promote-consts.after.mir | 23 +- ...wer_intrinsics.f_u64.PreCodegen.before.mir | 18 +- ...wer_intrinsics.forget.LowerIntrinsics.diff | 24 +- src/test/mir-opt/lower_intrinsics.rs | 2 +- ...fg-initial.after-ElaborateDrops.after.diff | 102 +- ...s.foo.MatchBranchSimplification.32bit.diff | 4 +- ...s.foo.MatchBranchSimplification.64bit.diff | 4 +- ...ed_if.MatchBranchSimplification.32bit.diff | 20 +- ...ed_if.MatchBranchSimplification.64bit.diff | 20 +- ...nators.test.MultipleReturnTerminators.diff | 8 +- ...egion_subtyping_basic.main.nll.0.32bit.mir | 71 +- ...egion_subtyping_basic.main.nll.0.64bit.mir | 71 +- ...wrap.SimplifyCfg-elaborate-drops.after.mir | 10 +- ...mplify_cfg.main.SimplifyCfg-early-opt.diff | 25 +- ...simplify_cfg.main.SimplifyCfg-initial.diff | 54 +- ...ain.SimplifyBranches-after-const-prop.diff | 16 +- ..._locals_fixedpoint.foo.SimplifyLocals.diff | 8 +- ...reachable.main.UnreachablePropagation.diff | 10 +- ...hable_asm.main.UnreachablePropagation.diff | 10 +- ...ble_asm_2.main.UnreachablePropagation.diff | 24 +- ...diverging.main.UnreachablePropagation.diff | 13 +- ...le_storage.while_loop.PreCodegen.after.mir | 14 +- .../pretty/expanded-and-path-remap-80832.pp | 13 + .../pretty/expanded-and-path-remap-80832.rs | 7 + .../archive-duplicate-names/foo.rs | 2 +- .../run-make-fulldeps/c-dynamic-dylib/foo.rs | 2 +- .../run-make-fulldeps/c-dynamic-rlib/foo.rs | 2 +- .../run-make-fulldeps/c-static-dylib/foo.rs | 2 +- .../run-make-fulldeps/c-static-rlib/foo.rs | 2 +- .../cdylib-dylib-linkage/foo.rs | 4 +- .../cdylib-fewer-symbols/foo.rs | 2 +- src/test/run-make-fulldeps/cdylib/foo.rs | 4 +- .../compiler-lookup-paths/d.rs | 2 +- .../compiler-rt-works-on-mingw/foo.rs | 8 +- .../coverage-llvmir/Makefile | 6 +- .../coverage-reports/Makefile | 9 +- .../expected_show_coverage.doctest.txt | 124 +- .../coverage-spanview/Makefile | 6 +- ...ort.main.-------.InstrumentCoverage.0.html | 31 +- ...ht_abort.-------.InstrumentCoverage.0.html | 99 +- ...ert.main.-------.InstrumentCoverage.0.html | 30 +- ...l_assert.-------.InstrumentCoverage.0.html | 14 +- ...osure#0}.-------.InstrumentCoverage.0.html | 10 +- ...osure#0}.-------.InstrumentCoverage.0.html | 22 +- ...osure#1}.-------.InstrumentCoverage.0.html | 22 +- ...osure#2}.-------.InstrumentCoverage.0.html | 22 +- ...sync.j-c.-------.InstrumentCoverage.0.html | 10 +- ...osure#0}.-------.InstrumentCoverage.0.html | 35 +- ...sure#10}.-------.InstrumentCoverage.0.html | 35 +- ...sure#11}.-------.InstrumentCoverage.0.html | 35 +- ...osure#1}.-------.InstrumentCoverage.0.html | 35 +- ...osure#2}.-------.InstrumentCoverage.0.html | 99 +- ...osure#3}.-------.InstrumentCoverage.0.html | 32 +- ...osure#5}.-------.InstrumentCoverage.0.html | 84 +- ...ons.main.-------.InstrumentCoverage.0.html | 437 +- ...ode.main.-------.InstrumentCoverage.0.html | 68 +- ...nused_fn.-------.InstrumentCoverage.0.html | 68 +- ..._library.-------.InstrumentCoverage.0.html | 68 +- ...est.main.-------.InstrumentCoverage.0.html | 103 +- ...doctests.-------.InstrumentCoverage.0.html | 194 +- ...ait.main.-------.InstrumentCoverage.0.html | 96 +- ...ics.main.-------.InstrumentCoverage.0.html | 175 +- .../if.main.-------.InstrumentCoverage.0.html | 159 +- ...lse.main.-------.InstrumentCoverage.0.html | 140 +- ...ems.main.-------.InstrumentCoverage.0.html | 117 +- ...ean.main.-------.InstrumentCoverage.0.html | 243 +- ...l#0}-fmt.-------.InstrumentCoverage.0.html | 53 +- ...ern.main.-------.InstrumentCoverage.0.html | 210 +- ...ops.main.-------.InstrumentCoverage.0.html | 105 +- ...low.main.-------.InstrumentCoverage.0.html | 342 +- ...overflow.-------.InstrumentCoverage.0.html | 640 +- ...ind.main.-------.InstrumentCoverage.0.html | 30 +- ...ht_panic.-------.InstrumentCoverage.0.html | 99 +- ...l#7}-fmt.-------.InstrumentCoverage.0.html | 56 +- ...oop.main.-------.InstrumentCoverage.0.html | 137 +- ...tch.main.-------.InstrumentCoverage.0.html | 198 +- ...oop.main.-------.InstrumentCoverage.0.html | 10 +- ...ult.call.-------.InstrumentCoverage.0.html | 12 +- ...ult.main.-------.InstrumentCoverage.0.html | 37 +- ...function.-------.InstrumentCoverage.0.html | 40 +- ...function.-------.InstrumentCoverage.0.html | 40 +- ...function.-------.InstrumentCoverage.0.html | 39 +- ...ret.main.-------.InstrumentCoverage.0.html | 40 +- .../coverage/coverage_tools.mk | 7 - .../run-make-fulldeps/coverage/doctest.rs | 43 +- .../staticlib.rs | 2 +- .../extern-fn-generic/test.rs | 14 +- .../extern-fn-generic/testcrate.rs | 8 +- .../extern-fn-mangle/test.rs | 10 +- .../extern-fn-struct-passing-abi/test.rs | 32 +- .../extern-fn-with-extern-types/test.rs | 2 +- .../extern-fn-with-packed-struct/test.rs | 4 +- .../extern-fn-with-union/test.rs | 10 +- .../extern-fn-with-union/testcrate.rs | 4 +- .../glibc-staticlib-args/library.rs | 2 +- .../interdependent-c-libraries/bar.rs | 6 +- .../interdependent-c-libraries/foo.rs | 6 +- src/test/run-make-fulldeps/issue-14500/foo.rs | 2 +- src/test/run-make-fulldeps/issue-15460/foo.rs | 2 +- .../run-make-fulldeps/issue-25581/test.rs | 4 +- src/test/run-make-fulldeps/issue-28595/a.rs | 2 +- src/test/run-make-fulldeps/issue-28595/b.rs | 7 +- .../link-cfg/dep-with-staticlib.rs | 2 +- src/test/run-make-fulldeps/link-cfg/dep.rs | 2 +- .../run-make-fulldeps/link-cfg/no-deps.rs | 2 +- .../run-make-fulldeps/link-path-order/main.rs | 8 +- .../linkage-attr-on-static/bar.rs | 2 +- .../long-linker-command-lines/foo.rs | 2 +- .../longjmp-across-rust/main.rs | 9 +- .../lto-no-link-whole-rlib/lib1.rs | 2 +- .../lto-no-link-whole-rlib/lib2.rs | 2 +- src/test/run-make-fulldeps/manual-link/foo.rs | 6 +- .../many-crates-but-no-match/Makefile | 7 +- .../no-duplicate-libs/main.rs | 2 +- .../sanitizer-cdylib-link/library.rs | 2 +- .../sanitizer-cdylib-link/program.rs | 2 +- .../sanitizer-dylib-link/library.rs | 2 +- .../sanitizer-dylib-link/program.rs | 2 +- .../sanitizer-staticlib-link/library.rs | 2 +- .../sanitizer-staticlib-link/program.rs | 2 +- .../save-analysis-fail/foo.rs | 112 +- .../split-debuginfo/Makefile | 59 + .../run-make-fulldeps/split-debuginfo/foo.rs | 1 + .../run-make-fulldeps/split-dwarf/Makefile | 2 +- .../static-dylib-by-default/bar.rs | 2 +- .../run-make-fulldeps/static-nobundle/bbb.rs | 2 +- .../staticlib-blank-lib/foo.rs | 2 +- .../run-make-fulldeps/std-core-cycle/foo.rs | 2 +- .../run-make-fulldeps/target-specs/foo.rs | 16 +- .../type-mismatch-same-crate-name/crateC.rs | 2 +- src/test/run-make/const_fn_mir/Makefile | 10 + src/test/run-make/const_fn_mir/dump.mir | 45 + src/test/run-make/const_fn_mir/main.rs | 10 + src/test/run-make/issue-36710/foo.rs | 4 +- src/test/run-make/wasm-import-module/bar.rs | 4 +- src/test/run-make/wasm-import-module/foo.rs | 2 +- src/test/run-pass-valgrind/osx-frameworks.rs | 6 +- src/test/rustdoc-js-std/primitive.js | 75 + src/test/rustdoc-js/primitive.js | 25 + src/test/rustdoc-js/primitive.rs | 5 + src/test/rustdoc-json/compare.py | 129 - src/test/rustdoc-json/nested.expected | 196 - src/test/rustdoc-json/nested.rs | 17 + src/test/rustdoc-json/structs.expected | 456 - src/test/rustdoc-json/structs.rs | 17 - src/test/rustdoc-json/structs/plain_empty.rs | 6 + src/test/rustdoc-json/structs/tuple.rs | 5 + src/test/rustdoc-json/structs/unit.rs | 5 + .../rustdoc-json/structs/with_generics.rs | 14 + .../rustdoc-json/structs/with_primitives.rs | 10 + src/test/rustdoc-json/traits/has_body.rs | 21 + src/test/rustdoc-json/unions/union.rs | 7 + src/test/rustdoc-ui/auxiliary/issue-61592.rs | 3 + .../check-doc-alias-attr-location.rs | 6 +- src/test/rustdoc-ui/coverage/basic.rs | 2 +- src/test/rustdoc-ui/deref-generic.rs | 15 + src/test/rustdoc-ui/deref-recursive-cycle.rs | 17 + src/test/rustdoc-ui/doc-alias-crate-level.rs | 2 - .../rustdoc-ui/doc-alias-crate-level.stderr | 4 +- src/test/rustdoc-ui/doc-alias-same-name.rs | 4 + .../rustdoc-ui/doc-alias-same-name.stderr | 8 + .../error-in-impl-trait/const-generics.rs | 1 - src/test/rustdoc-ui/ignore-block-help.rs | 7 + src/test/rustdoc-ui/ignore-block-help.stderr | 17 + .../auxiliary/pointer-reexports-allowed.rs | 4 + .../feature-gate-intra-doc-pointers.rs | 6 + .../feature-gate-intra-doc-pointers.stderr | 23 + .../incompatible-primitive-disambiguator.rs | 3 + ...ncompatible-primitive-disambiguator.stderr | 15 + .../intra-doc/non-path-primitives.rs | 35 + .../intra-doc/non-path-primitives.stderr | 69 + .../intra-doc/pointer-reexports-allowed.rs | 4 + .../intra-doc/unused-extern-crate.rs | 5 + .../intra-doc/unused-extern-crate.stderr | 15 + src/test/rustdoc-ui/invalid-syntax.stderr | 6 +- src/test/rustdoc-ui/issue-61592-2.rs | 10 + src/test/rustdoc-ui/issue-61592-2.stderr | 12 + src/test/rustdoc-ui/issue-61592.rs | 8 + src/test/rustdoc-ui/issue-61592.stderr | 11 + src/test/rustdoc-ui/issue-80992.rs | 11 + src/test/rustdoc-ui/issue-80992.stdout | 6 + src/test/rustdoc-ui/issue-81662-shortness.rs | 12 + .../rustdoc-ui/issue-81662-shortness.stdout | 16 + src/test/rustdoc-ui/range-pattern.rs | 3 + .../reference-link-reports-error-once.rs | 20 + .../reference-link-reports-error-once.stderr | 63 + src/test/rustdoc-ui/reference-links.rs | 6 + src/test/rustdoc-ui/reference-links.stderr | 14 + .../rustdoc-ui/run-directory.correct.stdout | 6 + .../rustdoc-ui/run-directory.incorrect.stdout | 6 + src/test/rustdoc-ui/run-directory.rs | 23 + src/test/rustdoc/async-fn.rs | 2 - src/test/rustdoc/auxiliary/issue-34274.rs | 2 +- src/test/rustdoc/auxiliary/issue-61592.rs | 4 + .../rustdoc/auxiliary/macro_pub_in_module.rs | 13 + .../const-generics/auxiliary/extern_crate.rs | 2 - .../const-generics/const-generics-docs.rs | 1 - src/test/rustdoc/const-generics/type-alias.rs | 1 - src/test/rustdoc/deref-recursive-pathbuf.rs | 26 + src/test/rustdoc/deref-recursive.rs | 42 + src/test/rustdoc/deref-typedef.rs | 21 +- src/test/rustdoc/fn-type.rs | 15 + src/test/rustdoc/for-lifetime.rs | 14 + src/test/rustdoc/foreigntype-reexport.rs | 10 +- src/test/rustdoc/foreigntype.rs | 2 +- .../rustdoc/implementor-stable-version.rs | 19 + .../inline_local/glob-extern-no-defaults.rs | 2 +- src/test/rustdoc/inline_local/glob-extern.rs | 2 +- src/test/rustdoc/inline_local/trait-vis.rs | 2 +- .../rustdoc/intra-doc-crate/auxiliary/self.rs | 3 + src/test/rustdoc/intra-doc-crate/self.rs | 3 + .../rustdoc/intra-doc/non-path-primitives.rs | 47 + .../intra-doc/primitive-disambiguators.rs | 4 + src/test/rustdoc/issue-22038.rs | 4 +- src/test/rustdoc/issue-61592.rs | 15 + src/test/rustdoc/issue-74083.rs | 2 +- .../issue-80233-normalize-auto-trait.rs | 37 + src/test/rustdoc/macro_pub_in_module.rs | 82 + src/test/rustdoc/macros.rs | 14 + src/test/rustdoc/mut-params.rs | 18 + src/test/rustdoc/range-arg-pattern.rs | 5 + src/test/rustdoc/reexport-check.rs | 2 +- src/test/rustdoc/remove-url-from-headings.rs | 2 +- src/test/rustdoc/sanitizer-option.rs | 2 +- src/test/rustdoc/titles.rs | 6 +- .../dropck-tarena-cycle-checked.rs | 4 +- .../ui-fulldeps/dropck_tarena_sound_drop.rs | 2 +- src/test/ui-fulldeps/issue-15149.rs | 6 +- src/test/ui-fulldeps/lint-tool-test.stderr | 18 +- src/test/ui-fulldeps/switch-stdout.rs | 5 +- src/test/ui/abi/anon-extern-mod.rs | 2 +- .../anon-extern-mod-cross-crate-1.rs | 4 +- src/test/ui/abi/auxiliary/foreign_lib.rs | 9 +- src/test/ui/abi/c-stack-as-value.rs | 2 +- src/test/ui/abi/cabi-int-widening.rs | 2 +- .../anon-extern-mod-cross-crate-1.rs | 4 +- .../anon-extern-mod-cross-crate-1.rs | 4 +- .../auxiliary/extern-crosscrate-source.rs | 19 +- src/test/ui/abi/extern/extern-call-deep.rs | 17 +- src/test/ui/abi/extern/extern-call-deep2.rs | 23 +- src/test/ui/abi/extern/extern-call-direct.rs | 2 +- .../ui/abi/extern/extern-call-indirect.rs | 17 +- src/test/ui/abi/extern/extern-call-scrub.rs | 23 +- src/test/ui/abi/extern/extern-pass-TwoU16s.rs | 7 +- src/test/ui/abi/extern/extern-pass-TwoU32s.rs | 7 +- src/test/ui/abi/extern/extern-pass-TwoU64s.rs | 7 +- src/test/ui/abi/extern/extern-pass-TwoU8s.rs | 7 +- src/test/ui/abi/extern/extern-pass-char.rs | 3 +- src/test/ui/abi/extern/extern-pass-double.rs | 2 +- src/test/ui/abi/extern/extern-pass-empty.rs | 6 +- src/test/ui/abi/extern/extern-pass-u32.rs | 3 +- src/test/ui/abi/extern/extern-pass-u64.rs | 3 +- .../ui/abi/extern/extern-return-TwoU16s.rs | 5 +- .../ui/abi/extern/extern-return-TwoU32s.rs | 5 +- .../ui/abi/extern/extern-return-TwoU64s.rs | 5 +- .../ui/abi/extern/extern-return-TwoU8s.rs | 5 +- .../ui/abi/foreign/auxiliary/foreign_lib.rs | 9 +- .../ui/abi/foreign/foreign-call-no-runtime.rs | 29 +- .../ui/abi/foreign/foreign-fn-with-byval.rs | 8 +- src/test/ui/abi/foreign/foreign-no-abi.rs | 2 +- src/test/ui/{issues => abi}/issue-28676.rs | 25 +- .../ui/abi/issues/issue-22565-rust-call.rs | 26 +- .../abi/issues/issue-22565-rust-call.stderr | 22 +- .../issues/issue-62350-sysv-neg-reg-counts.rs | 16 +- .../ui/abi/mir/mir_codegen_calls_variadic.rs | 13 +- src/test/ui/abi/segfault-no-out-of-stack.rs | 18 +- src/test/ui/abi/stack-probes.rs | 6 +- src/test/ui/abi/statics/static-mut-foreign.rs | 2 +- src/test/ui/abi/struct-enums/struct-return.rs | 58 +- src/test/ui/abi/variadic-ffi.rs | 6 +- src/test/ui/allocator/object-safe.rs | 13 + .../array-break-length.rs | 0 .../array-break-length.stderr | 0 .../{ => array-slice-vec}/array-not-vector.rs | 0 .../array-not-vector.stderr | 0 .../array_const_index-0.rs | 1 + .../array_const_index-0.stderr | 2 + .../ui/array-slice-vec/array_const_index-1.rs | 15 +- .../array_const_index-1.stderr | 2 + .../ui/array-slice-vec/array_const_index-2.rs | 12 + .../ui/array-slice-vec/copy-out-of-array-1.rs | 2 +- .../issue-15730.rs | 0 .../issue-18425.rs | 0 .../match_arr_unknown_len.stderr | 1 - .../ui/array-slice-vec/repeat_empty_ok.rs | 15 + .../ui/array-slice-vec/repeat_empty_ok.stderr | 19 + src/test/ui/{ => array-slice-vec}/slice-2.rs | 0 .../ui/{ => array-slice-vec}/slice-2.stderr | 0 .../ui/{ => array-slice-vec}/slice-mut-2.rs | 0 .../{ => array-slice-vec}/slice-mut-2.stderr | 0 .../ui/{ => array-slice-vec}/slice-mut.rs | 0 .../ui/{ => array-slice-vec}/slice-mut.stderr | 0 .../slice-to-vec-comparison.rs | 0 .../slice-to-vec-comparison.stderr | 0 .../vec-macro-with-comma-only.rs | 0 .../vec-macro-with-comma-only.stderr | 0 .../vec-mut-iter-borrow.rs | 0 .../vec-mut-iter-borrow.stderr | 0 .../{vec => array-slice-vec}/vec-overrun.rs | 0 .../{vec => array-slice-vec}/vec-res-add.rs | 0 .../vec-res-add.stderr | 0 .../vector-cast-weirdness.rs | 0 .../vector-cast-weirdness.stderr | 0 .../ui/{ => array-slice-vec}/vector-no-ann.rs | 0 .../vector-no-ann.stderr | 0 src/test/ui/array_const_index-1.rs | 8 - .../associated-const-type-parameter-arrays.rs | 5 +- ...ociated-const-type-parameter-arrays.stderr | 16 +- .../defaults-not-assumed-fail.rs | 4 +- .../defaults-not-assumed-fail.stderr | 16 +- ...9-assoc-const-static-recursion-impl.stderr | 2 +- ...onst-static-recursion-trait-default.stderr | 2 +- ...-assoc-const-static-recursion-trait.stderr | 2 +- .../associated-item-duplicate-bounds.rs | 2 +- .../associated-item-duplicate-bounds.stderr | 10 +- .../associated-item-two-bounds.rs | 16 + ...ted-types-projection-to-unrelated-trait.rs | 2 +- .../ui/associated-types/defaults-wf.stderr | 2 +- .../higher-ranked-projection.bad.stderr | 7 +- .../hr-associated-type-bound-2.rs | 2 +- .../hr-associated-type-bound-2.stderr | 6 +- .../issue-18655.rs | 0 .../issue-22037.rs | 0 .../issue-22037.stderr | 0 .../associated-types}/issue-23595-1.rs | 0 .../ui/associated-types/issue-23595-1.stderr | 16 + src/test/ui/associated-types/issue-24159.rs | 37 + .../issue-24338.rs | 0 .../issue-27675-unchecked-bounds.rs | 0 .../issue-27675-unchecked-bounds.stderr | 17 + src/test/ui/associated-types/issue-37808.rs | 19 + src/test/ui/associated-types/issue-37883.rs | 25 + src/test/ui/associated-types/issue-38917.rs | 25 + src/test/ui/associated-types/issue-39532.rs | 14 + src/test/ui/associated-types/issue-40093.rs | 14 + src/test/ui/associated-types/issue-43475.rs | 10 + .../issue-48551.rs | 0 .../issue-50301.rs | 0 src/test/ui/associated-types/issue-63591.rs | 24 + .../object-method-numbering.rs | 0 .../ast-json/ast-json-noexpand-output.stdout | 2 +- src/test/ui/ast-json/ast-json-output.stdout | 2 +- .../edition-deny-async-fns-2015.rs | 18 +- .../edition-deny-async-fns-2015.stderr | 36 +- .../async-await/feature-async-closure.stderr | 1 + src/test/ui/async-await/issue-73137.rs | 1 - .../{issues => async-await}/issue-73541-2.rs | 0 .../issue-73541-2.stderr | 0 .../issue-76547.nll.stderr | 0 .../ui/{issues => async-await}/issue-76547.rs | 0 .../issue-76547.stderr | 0 .../async-await/issues/issue-62097.nll.stderr | 3 +- src/test/ui/async-await/issues/issue-65159.rs | 3 +- .../ui/async-await/issues/issue-65159.stderr | 18 +- .../issues/issue-78654.full.stderr | 4 +- .../async-await/issues/issue-78654.min.stderr | 4 +- src/test/ui/async-await/issues/issue-78654.rs | 1 - .../issues/issue-78938-async-block.rs | 33 + .../issues/issue-78938-async-block.stderr | 21 + .../suggest-switching-edition-on-await.stderr | 8 +- src/test/ui/async-await/unused-lifetime.rs | 79 +- .../ui/async-await/unused-lifetime.stderr | 30 +- src/test/ui/attr-usage-inline.rs | 16 + src/test/ui/attr-usage-inline.stderr | 8 +- src/test/ui/attributes/item-attributes.rs | 41 +- .../attributes/key-value-expansion-on-mac.rs | 15 + .../key-value-expansion-on-mac.stderr | 8 + src/test/ui/attributes/key-value-non-ascii.rs | 3 +- .../ui/attributes/key-value-non-ascii.stderr | 13 +- src/test/ui/attributes/obsolete-attr.rs | 2 +- src/test/ui/attributes/obsolete-attr.stderr | 2 +- .../issue-38940.rs | 0 .../issue-38940.stderr | 0 src/test/ui/auxiliary/extern-statics.rs | 2 +- .../link-cfg-works-transitive-dylib.rs | 2 +- .../link-cfg-works-transitive-rlib.rs | 2 +- .../ui/auxiliary/lto-duplicate-symbols1.rs | 2 +- .../ui/auxiliary/lto-duplicate-symbols2.rs | 2 +- .../auxiliary/trait_superkinds_in_metadata.rs | 8 - src/test/ui/backtrace-apple-no-dsymutil.rs | 30 + src/test/ui/bad/bad-extern-link-attrs.rs | 2 +- src/test/ui/bad/bad-mid-path-type-params.rs | 11 +- .../ui/bad/bad-mid-path-type-params.stderr | 74 +- src/test/ui/bad/bad-sized.stderr | 2 +- src/test/ui/binding/const-param.full.stderr | 9 + src/test/ui/binding/const-param.min.stderr | 9 + src/test/ui/binding/const-param.rs | 5 +- src/test/ui/binding/const-param.stderr | 19 - .../{ => binop}/binary-minus-without-space.rs | 0 .../{ => binop}/binary-op-on-double-ref.fixed | 0 .../ui/{ => binop}/binary-op-on-double-ref.rs | 0 .../binary-op-on-double-ref.stderr | 0 src/test/ui/{ => binop}/binops-issue-22743.rs | 0 src/test/ui/{ => binop}/binops.rs | 0 src/test/ui/{issues => binop}/issue-25916.rs | 0 src/test/ui/{issues => binop}/issue-28837.rs | 0 .../ui/{issues => binop}/issue-28837.stderr | 0 .../borrow-raw-address-of-mutability.stderr | 4 +- .../ui/{issues => borrowck}/issue-17263.rs | 0 .../ui/{issues => borrowck}/issue-17545.rs | 0 .../{issues => borrowck}/issue-17545.stderr | 0 .../ui/{issues => borrowck}/issue-20801.rs | 0 .../{issues => borrowck}/issue-20801.stderr | 0 .../issue-24267-flow-exit.rs | 0 .../issue-24267-flow-exit.stderr | 0 .../ui/{issues => borrowck}/issue-25793.rs | 0 .../{issues => borrowck}/issue-25793.stderr | 0 ...issue-27282-move-match-input-into-guard.rs | 0 ...e-27282-move-match-input-into-guard.stderr | 0 ...sue-27282-mutate-before-diverging-arm-2.rs | 0 ...27282-mutate-before-diverging-arm-2.stderr | 0 .../issue-27282-reborrow-ref-mut-in-guard.rs | 0 ...sue-27282-reborrow-ref-mut-in-guard.stderr | 0 .../ui/{issues => borrowck}/issue-33819.rs | 0 .../{issues => borrowck}/issue-33819.stderr | 0 .../ui/{issues => borrowck}/issue-45199.rs | 0 .../{issues => borrowck}/issue-45199.stderr | 0 src/test/ui/borrowck/issue-45983.stderr | 1 + .../ui/{issues => borrowck}/issue-46471.rs | 0 .../{issues => borrowck}/issue-46471.stderr | 0 src/test/ui/borrowck/issue-7573.stderr | 1 + .../borrowck/move-in-pattern-mut-in-loop.rs | 10 + .../move-in-pattern-mut-in-loop.stderr | 15 + .../borrowck/regions-escape-bound-fn-2.stderr | 1 + .../borrowck/regions-escape-bound-fn.stderr | 1 + .../regions-escape-unboxed-closure.stderr | 1 + src/test/ui/bound-suggestions.fixed | 27 +- src/test/ui/bound-suggestions.rs | 27 +- src/test/ui/bound-suggestions.stderr | 82 +- ...ltin-superkinds-capabilities-transitive.rs | 0 .../builtin-superkinds-capabilities-xc.rs | 0 .../builtin-superkinds-capabilities.rs | 0 .../builtin-superkinds-in-metadata2.rs} | 0 .../builtin-superkinds-phantom-typaram.rs | 0 .../builtin-superkinds-simple2.rs} | 0 .../builtin-superkinds-typaram.rs | 0 src/test/ui/c-stack-returning-int64.rs | 8 +- src/test/ui/c-variadic/variadic-ffi-1.rs | 8 +- .../c-variadic/variadic-ffi-no-fixed-args.rs | 4 +- src/test/ui/{ => cast}/cast-char.rs | 0 src/test/ui/{ => cast}/cast-char.stderr | 0 src/test/ui/{ => cast}/cast-does-fallback.rs | 0 src/test/ui/{ => cast}/cast-region-to-uint.rs | 0 .../{ => cast}/cast-rfc0401-vtable-kinds.rs | 0 src/test/ui/{ => cast}/cast-rfc0401.rs | 0 src/test/ui/{ => cast}/cast-to-infer-ty.rs | 0 src/test/ui/{ => cast}/cast.rs | 0 .../ui/{ => cast}/casts-differing-anon.rs | 0 .../ui/{ => cast}/casts-differing-anon.stderr | 0 src/test/ui/{ => cast}/casts-issue-46365.rs | 0 .../ui/{ => cast}/casts-issue-46365.stderr | 0 src/test/ui/{ => cast}/fat-ptr-cast-rpass.rs | 0 src/test/ui/{ => cast}/fat-ptr-cast.rs | 0 src/test/ui/{ => cast}/fat-ptr-cast.stderr | 0 src/test/ui/{issues => cast}/issue-17444.rs | 0 .../ui/{issues => cast}/issue-17444.stderr | 0 src/test/ui/cast/unsupported-cast.rs | 5 + .../ui/{ => cast}/unsupported-cast.stderr | 4 +- .../assume-incomplete.rs | 38 + .../auxiliary/ver-cfg-rel.rs | 56 + src/test/ui/cfg/conditional-compile.rs | 58 +- src/test/ui/check-doc-alias-attr-location.rs | 7 +- .../ui/check-doc-alias-attr-location.stderr | 8 +- src/test/ui/check-doc-alias-attr.rs | 1 - src/test/ui/check-doc-alias-attr.stderr | 18 +- .../ui/check-static-immutable-mut-slices.rs | 2 +- .../check-static-immutable-mut-slices.stderr | 4 +- .../2229_closure_analysis/by_value.rs | 41 + .../2229_closure_analysis/by_value.stderr | 67 + .../capture-analysis-1.rs | 35 + .../capture-analysis-1.stderr | 77 + .../capture-analysis-2.rs | 30 + .../capture-analysis-2.stderr | 65 + .../capture-analysis-3.rs | 35 + .../capture-analysis-3.stderr | 65 + .../capture-analysis-4.rs | 33 + .../capture-analysis-4.stderr | 62 + .../deep-multilevel-struct.rs | 52 + .../deep-multilevel-struct.stderr | 70 + .../deep-multilevel-tuple.rs | 27 + .../deep-multilevel-tuple.stderr | 70 + .../diagnostics/cant-mutate-imm-borrow.rs | 20 + .../diagnostics/cant-mutate-imm-borrow.stderr | 21 + .../diagnostics/cant-mutate-imm.rs | 35 + .../diagnostics/cant-mutate-imm.stderr | 30 + ...losure-origin-multi-variant-diagnostics.rs | 31 + ...re-origin-multi-variant-diagnostics.stderr | 26 + ...osure-origin-single-variant-diagnostics.rs | 26 + ...e-origin-single-variant-diagnostics.stderr | 37 + .../closure-origin-struct-diagnostics.rs | 25 + .../closure-origin-struct-diagnostics.stderr | 26 + .../closure-origin-tuple-diagnostics-1.rs | 16 + .../closure-origin-tuple-diagnostics-1.stderr | 26 + .../closure-origin-tuple-diagnostics.rs | 15 + .../closure-origin-tuple-diagnostics.stderr | 23 + .../diagnostics/mut_ref.rs | 38 + .../diagnostics/mut_ref.stderr | 33 + .../migrations/insignificant_drop.rs | 130 + .../migrations/insignificant_drop.stderr | 105 + .../migrations/no_migrations.rs | 84 + .../migrations/significant_drop.rs | 137 + .../migrations/significant_drop.stderr | 103 + .../2229_closure_analysis/move_closure.rs | 72 + .../2229_closure_analysis/move_closure.stderr | 147 + .../run_pass/by_value.rs | 28 + .../run_pass/by_value.stderr | 11 + .../run_pass/fru_syntax.rs | 46 + .../run_pass/fru_syntax.stderr | 11 + .../run_pass/move_closure.rs | 64 + .../run_pass/move_closure.stderr | 11 + .../2229_closure_analysis/run_pass/mut_ref.rs | 56 + .../run_pass/mut_ref.stderr | 11 + .../run_pass/mut_ref_struct_mem.rs | 45 + .../run_pass/mut_ref_struct_mem.stderr | 11 + .../run_pass/unsafe_ptr.rs | 47 + .../run_pass/unsafe_ptr.stderr | 11 + .../simple-struct-min-capture.rs | 4 +- .../simple-struct-min-capture.stderr | 5 +- .../2229_closure_analysis/unsafe_ptr.rs | 63 + .../2229_closure_analysis/unsafe_ptr.stderr | 102 + .../expect-region-supply-region.stderr | 1 + src/test/ui/closures/closure-no-fn-1.stderr | 5 + src/test/ui/closures/closure-no-fn-2.stderr | 5 + src/test/ui/closures/closure-no-fn-4.rs | 8 + src/test/ui/closures/closure-no-fn-4.stderr | 24 + src/test/ui/closures/closure-no-fn-5.rs | 12 + src/test/ui/closures/closure-no-fn-5.stderr | 23 + .../ui/closures/closure-reform-bad.stderr | 5 + .../coerce-unsafe-closure-to-unsafe-fn-ptr.rs | 0 ...rce-unsafe-closure-to-unsafe-fn-ptr.stderr | 11 + .../closures}/coerce-unsafe-to-closure.rs | 0 .../closures/coerce-unsafe-to-closure.stderr | 11 + .../issue-80313-mutable-borrow-in-closure.rs | 7 + ...sue-80313-mutable-borrow-in-closure.stderr | 14 + ...ue-80313-mutable-borrow-in-move-closure.rs | 7 + ...0313-mutable-borrow-in-move-closure.stderr | 14 + .../issue-80313-mutation-in-closure.rs | 7 + .../issue-80313-mutation-in-closure.stderr | 14 + .../issue-80313-mutation-in-move-closure.rs | 7 + ...ssue-80313-mutation-in-move-closure.stderr | 14 + .../ui/closures/issue-81700-mut-borrow.rs | 5 + .../ui/closures/issue-81700-mut-borrow.stderr | 13 + src/test/ui/closures/local-type-mix.rs | 17 + src/test/ui/closures/local-type-mix.stderr | 51 + .../old-closure-arg-call-as.rs} | 0 .../old-closure-arg.rs} | 0 .../old-closure-explicit-types.rs} | 0 .../old-closure-expr-precedence.rs} | 0 .../old-closure-expr-precedence.stderr} | 2 +- ...closure-expression-remove-semicolon.fixed} | 0 ...ld-closure-expression-remove-semicolon.rs} | 0 ...losure-expression-remove-semicolon.stderr} | 2 +- .../old-closure-fn-coerce.rs} | 0 .../old-closure-iter-1.rs} | 0 .../old-closure-iter-2.rs} | 0 .../print/closure-print-verbose.stderr | 5 + .../cmse-nonsecure-call/gate_test.rs | 11 + .../cmse-nonsecure-call/gate_test.stderr | 12 + .../params-on-registers.rs | 15 + .../cmse-nonsecure-call/params-on-stack.rs | 17 + .../params-on-stack.stderr | 5 + .../wrong-abi-location-1.rs | 6 + .../wrong-abi-location-1.stderr | 9 + .../wrong-abi-location-2.rs | 8 + .../wrong-abi-location-2.stderr | 11 + .../cmse-nonsecure-entry/gate_test.rs | 0 .../cmse-nonsecure-entry/gate_test.stderr | 0 .../params-on-registers.rs | 0 .../cmse-nonsecure-entry/params-on-stack.rs | 0 .../params-on-stack.stderr | 0 .../cmse-nonsecure-entry/trustzone-only.rs | 0 .../trustzone-only.stderr | 0 .../cmse-nonsecure-entry/wrong-abi.rs | 0 .../cmse-nonsecure-entry/wrong-abi.stderr | 0 src/test/ui/codemap_tests/tab_3.stderr | 2 +- src/test/ui/codemap_tests/unicode.stderr | 2 +- src/test/ui/command/command-current-dir.rs | 49 + src/test/ui/command/command-setgroups.rs | 27 + .../const-generics/apit-with-const-param.rs | 1 - src/test/ui/const-generics/arg-in-pat-1.rs | 23 + src/test/ui/const-generics/arg-in-pat-2.rs | 10 + src/test/ui/const-generics/arg-in-pat-3.rs | 43 + .../const-generics/argument_order.full.stderr | 4 +- .../const-generics/argument_order.min.stderr | 8 +- src/test/ui/const-generics/argument_order.rs | 1 - .../array-impls/into-iter-impls-length-32.rs | 1 - .../array-impls/into-iter-impls-length-33.rs | 1 - ...y-size-in-generic-struct-param.full.stderr | 4 +- ...ay-size-in-generic-struct-param.min.stderr | 8 +- .../array-size-in-generic-struct-param.rs | 1 - .../array-wrapper-struct-ctor.rs | 1 - .../associated-type-bound-fail.full.stderr | 2 +- .../associated-type-bound-fail.min.stderr | 2 +- .../associated-type-bound-fail.rs | 1 - .../const-generics/associated-type-bound.rs | 1 - .../auxiliary/const_generic_lib.rs | 1 - .../ui/const-generics/auxiliary/crayte.rs | 1 - .../ui/const-generics/auxiliary/impl-const.rs | 1 - src/test/ui/const-generics/broken-mir-1.rs | 1 - src/test/ui/const-generics/broken-mir-2.rs | 1 - .../cannot-infer-type-for-const-param.rs | 1 - .../closing-args-token.full.stderr | 10 +- .../closing-args-token.min.stderr | 10 +- .../ui/const-generics/closing-args-token.rs | 1 - .../ui/const-generics/coerce_unsized_array.rs | 1 - .../concrete-const-as-fn-arg.rs | 1 - .../concrete-const-impl-method.rs | 1 - .../condition-in-trait-const-arg.rs | 1 - .../const-arg-in-const-arg.min.stderr | 46 +- .../const-generics/const-arg-in-const-arg.rs | 1 - src/test/ui/const-generics/const-arg-in-fn.rs | 1 - .../const-arg-type-arg-misordered.full.stderr | 2 +- .../const-arg-type-arg-misordered.min.stderr | 2 +- .../const-arg-type-arg-misordered.rs | 1 - .../const-argument-if-length.full.stderr | 4 +- .../const-argument-if-length.min.stderr | 4 +- .../const-argument-if-length.rs | 1 - .../const-expression-parameter.full.stderr | 2 +- .../const-expression-parameter.min.stderr | 2 +- .../const-expression-parameter.rs | 1 - .../const-fn-with-const-param.rs | 1 - .../const-generic-array-wrapper.rs | 1 - .../const-generics/const-generic-type_name.rs | 1 - .../const-param-after-const-literal-arg.rs | 1 - ...onst-param-before-other-params.full.stderr | 2 +- ...const-param-before-other-params.min.stderr | 12 +- .../const-param-before-other-params.rs | 1 - .../const-param-elided-lifetime.full.stderr | 10 +- .../const-param-elided-lifetime.min.stderr | 30 +- .../const-param-elided-lifetime.rs | 1 - .../const-param-from-outer-fn.full.stderr | 2 +- .../const-param-from-outer-fn.min.stderr | 2 +- .../const-param-from-outer-fn.rs | 1 - .../ui/const-generics/const-param-hygiene.rs | 1 - .../ui/const-generics/const-param-in-async.rs | 1 - .../const-param-in-trait-ungated.rs | 3 - .../const-param-in-trait-ungated.stderr | 12 - .../ui/const-generics/const-param-in-trait.rs | 1 - .../const-generics/const-param-shadowing.rs | 2 - .../const-param-shadowing.stderr | 2 +- ...am-type-depends-on-const-param.full.stderr | 4 +- ...ram-type-depends-on-const-param.min.stderr | 12 +- ...const-param-type-depends-on-const-param.rs | 1 - ...aram-type-depends-on-type-param-ungated.rs | 2 +- ...-type-depends-on-type-param-ungated.stderr | 14 +- ...ram-type-depends-on-type-param.full.stderr | 4 +- ...aram-type-depends-on-type-param.min.stderr | 4 +- .../const-param-type-depends-on-type-param.rs | 1 - ...const-parameter-uppercase-lint.full.stderr | 4 +- .../const-parameter-uppercase-lint.min.stderr | 4 +- .../const-parameter-uppercase-lint.rs | 1 - src/test/ui/const-generics/const-types.rs | 1 - .../cross_crate_predicate.stderr | 8 +- .../different-fn.stderr | 2 +- .../elaborate-trait-pred.rs | 24 + .../const_evaluatable_checked/eval-privacy.rs | 31 + .../eval-privacy.stderr | 43 + ...gate-const_evaluatable_checked.full.stderr | 2 +- ...-gate-const_evaluatable_checked.min.stderr | 2 +- .../feature-gate-const_evaluatable_checked.rs | 1 - .../nested-abstract-consts-1.rs | 24 + .../nested-abstract-consts-2.rs | 35 + .../nested_uneval_unification-1.rs | 34 + .../nested_uneval_unification-2.rs | 29 + .../simple.min.stderr | 4 +- .../const_evaluatable_checked/simple.rs | 1 - .../simple_fail.full.stderr | 10 +- .../simple_fail.min.stderr | 13 +- .../const_evaluatable_checked/simple_fail.rs | 7 +- .../subexprs_are_const_evalutable.rs | 17 + .../ty-alias-substitution.rs | 14 + src/test/ui/const-generics/core-types.rs | 1 - .../ui/const-generics/cross_crate_complex.rs | 1 - .../defaults/complex-unord-param.min.stderr | 4 +- .../defaults/complex-unord-param.rs | 1 - .../defaults/intermixed-lifetime.full.stderr | 8 +- .../defaults/intermixed-lifetime.min.stderr | 16 +- .../defaults/intermixed-lifetime.rs | 1 - .../defaults/needs-feature.min.stderr | 4 +- .../defaults/needs-feature.none.stderr | 16 +- .../const-generics/defaults/needs-feature.rs | 7 +- .../defaults/simple-defaults.min.stderr | 4 +- .../defaults/simple-defaults.rs | 1 - .../defaults/wrong-order.full.stderr | 3 +- .../defaults/wrong-order.min.stderr | 2 +- .../ui/const-generics/defaults/wrong-order.rs | 1 - .../derive-debug-array-wrapper.rs | 1 - .../different_byref.full.stderr | 2 +- .../const-generics/different_byref.min.stderr | 4 +- src/test/ui/const-generics/different_byref.rs | 1 - .../different_byref_simple.full.stderr | 2 +- .../different_byref_simple.min.stderr | 2 +- .../const-generics/different_byref_simple.rs | 1 - src/test/ui/const-generics/dyn-supertraits.rs | 1 - .../exhaustive-value.full.stderr | 2 +- .../exhaustive-value.min.stderr | 2 +- .../ui/const-generics/exhaustive-value.rs | 1 - .../fn-const-param-call.full.stderr | 4 +- .../fn-const-param-call.min.stderr | 4 +- .../ui/const-generics/fn-const-param-call.rs | 1 - .../fn-const-param-infer.full.stderr | 2 +- .../fn-const-param-infer.min.stderr | 2 +- .../ui/const-generics/fn-const-param-infer.rs | 1 - .../fn-taking-const-generic-array.rs | 1 - ...bid-non-structural_match-types.full.stderr | 2 +- ...rbid-non-structural_match-types.min.stderr | 10 +- .../forbid-non-structural_match-types.rs | 1 - .../foreign-item-const-parameter.full.stderr | 4 +- .../foreign-item-const-parameter.min.stderr | 4 +- .../foreign-item-const-parameter.rs | 1 - ...-function-call-in-array-length.full.stderr | 2 +- ...c-function-call-in-array-length.min.stderr | 4 +- .../generic-function-call-in-array-length.rs | 1 - .../generic-param-mismatch.full.stderr | 2 +- .../generic-param-mismatch.min.stderr | 2 +- .../const-generics/generic-param-mismatch.rs | 1 - .../generic-sum-in-array-length.full.stderr | 2 +- .../generic-sum-in-array-length.min.stderr | 4 +- .../generic-sum-in-array-length.rs | 1 - .../impl-const-generic-struct.rs | 1 - ...mpl-trait-with-const-arguments.full.stderr | 2 +- ...impl-trait-with-const-arguments.min.stderr | 2 +- .../impl-trait-with-const-arguments.rs | 1 - ...incorrect-number-of-const-args.full.stderr | 32 +- .../incorrect-number-of-const-args.min.stderr | 32 +- .../incorrect-number-of-const-args.rs | 8 +- .../infer/cannot-infer-const-args.full.stderr | 2 +- .../infer/cannot-infer-const-args.min.stderr | 2 +- .../infer/cannot-infer-const-args.rs | 1 - .../ui/const-generics/infer/issue-77092.rs | 2 - .../const-generics/infer/issue-77092.stderr | 2 +- .../infer/method-chain.full.stderr | 2 +- .../infer/method-chain.min.stderr | 2 +- .../ui/const-generics/infer/method-chain.rs | 1 - .../infer/uninferred-consts.full.stderr | 2 +- .../infer/uninferred-consts.min.stderr | 2 +- .../const-generics/infer/uninferred-consts.rs | 1 - .../ui/const-generics/infer_arg_from_pat.rs | 1 - .../const-generics/infer_arr_len_from_pat.rs | 1 - ...ger-literal-generic-arg-in-where-clause.rs | 1 - ...cs-type_name-as-const-argument.full.stderr | 2 +- ...ics-type_name-as-const-argument.min.stderr | 6 +- .../intrinsics-type_name-as-const-argument.rs | 1 - .../invalid-const-arg-for-type-param.rs | 11 +- .../invalid-const-arg-for-type-param.stderr | 30 +- .../invalid-constant-in-args.rs | 2 +- .../invalid-constant-in-args.stderr | 14 +- .../issue-61522-array-len-succ.full.stderr | 4 +- .../issue-61522-array-len-succ.min.stderr | 4 +- .../issue-61522-array-len-succ.rs | 1 - ...96-impl-trait-for-str-const-arg.min.stderr | 4 +- ...ssue-66596-impl-trait-for-str-const-arg.rs | 1 - .../ui/const-generics/issue-67375.full.stderr | 4 +- .../ui/const-generics/issue-67375.min.stderr | 4 +- src/test/ui/const-generics/issue-67375.rs | 1 - .../const-generics/issue-67945-1.full.stderr | 4 +- .../const-generics/issue-67945-1.min.stderr | 6 +- src/test/ui/const-generics/issue-67945-1.rs | 1 - .../const-generics/issue-67945-2.full.stderr | 4 +- .../const-generics/issue-67945-2.min.stderr | 6 +- src/test/ui/const-generics/issue-67945-2.rs | 1 - .../const-generics/issue-67945-3.full.stderr | 2 +- .../const-generics/issue-67945-3.min.stderr | 2 +- src/test/ui/const-generics/issue-67945-3.rs | 1 - .../issue-68104-print-stack-overflow.rs | 1 - .../issue-70180-1-stalled_on.rs | 1 - .../issue-70180-2-stalled_on.rs | 1 - src/test/ui/const-generics/issue-71202.rs | 33 + src/test/ui/const-generics/issue-71986.rs | 1 - .../{issues => const-generics}/issue-73899.rs | 0 src/test/ui/const-generics/issue-74906.rs | 1 - .../{issues => const-generics}/issue-75763.rs | 0 .../auxiliary/const_generic_issues_lib.rs | 1 - .../issues/issue-56445.full.stderr | 3 +- .../issues/issue-56445.min.stderr | 2 +- .../ui/const-generics/issues/issue-56445.rs | 1 - .../ui/const-generics/issues/issue-60263.rs | 9 - .../const-generics/issues/issue-60263.stderr | 12 - ...ssue-60818-struct-constructors.full.stderr | 1 - .../issues/issue-60818-struct-constructors.rs | 1 - .../issues/issue-61336-1.full.stderr | 1 - .../ui/const-generics/issues/issue-61336-1.rs | 1 - .../issues/issue-61336-2.full.stderr | 3 +- .../issues/issue-61336-2.min.stderr | 2 +- .../ui/const-generics/issues/issue-61336-2.rs | 1 - .../issues/issue-61336.full.stderr | 3 +- .../issues/issue-61336.min.stderr | 2 +- .../ui/const-generics/issues/issue-61336.rs | 1 - .../issues/issue-61422.full.stderr | 1 - .../ui/const-generics/issues/issue-61422.rs | 1 - .../issues/issue-61432.full.stderr | 1 - .../ui/const-generics/issues/issue-61432.rs | 1 - .../issues/issue-61747.full.stderr | 3 +- .../issues/issue-61747.min.stderr | 2 +- .../ui/const-generics/issues/issue-61747.rs | 1 - .../issues/issue-61935.full.stderr | 2 +- .../issues/issue-61935.min.stderr | 2 +- .../ui/const-generics/issues/issue-61935.rs | 1 - ...sue-62187-encountered-polymorphic-const.rs | 1 - .../issues/issue-62220.full.stderr | 2 +- .../issues/issue-62220.min.stderr | 2 +- .../ui/const-generics/issues/issue-62220.rs | 1 - .../issues/issue-62456.full.stderr | 2 +- .../issues/issue-62456.min.stderr | 2 +- .../ui/const-generics/issues/issue-62456.rs | 1 - .../issues/issue-62504.full.stderr | 2 +- .../issues/issue-62504.min.stderr | 4 +- .../ui/const-generics/issues/issue-62504.rs | 1 - .../issues/issue-62579-no-match.min.stderr | 4 +- .../issues/issue-62579-no-match.rs | 1 - .../issues/issue-62878.full.stderr | 6 +- .../issues/issue-62878.min.stderr | 6 +- .../ui/const-generics/issues/issue-62878.rs | 1 - .../issues/issue-63322-forbid-dyn.full.stderr | 2 +- .../issues/issue-63322-forbid-dyn.min.stderr | 6 +- .../issues/issue-63322-forbid-dyn.rs | 1 - .../issues/issue-64494.full.stderr | 4 +- .../issues/issue-64494.min.stderr | 6 +- .../ui/const-generics/issues/issue-64494.rs | 1 - .../ui/const-generics/issues/issue-64519.rs | 1 - .../issues/issue-66205.full.stderr | 2 +- .../issues/issue-66205.min.stderr | 2 +- .../ui/const-generics/issues/issue-66205.rs | 1 - .../ui/const-generics/issues/issue-66906.rs | 1 - .../ui/const-generics/issues/issue-67185-1.rs | 1 - .../issues/issue-67185-2.full.stderr | 12 +- .../issues/issue-67185-2.min.stderr | 12 +- .../ui/const-generics/issues/issue-67185-2.rs | 1 - .../issues/issue-67739.full.stderr | 2 +- .../issues/issue-67739.min.stderr | 2 +- .../ui/const-generics/issues/issue-67739.rs | 1 - .../issues/issue-68366.full.stderr | 4 +- .../issues/issue-68366.min.stderr | 6 +- .../ui/const-generics/issues/issue-68366.rs | 1 - .../ui/const-generics/issues/issue-68596.rs | 1 - .../issues/issue-68615-adt.min.stderr | 4 +- .../const-generics/issues/issue-68615-adt.rs | 1 - .../issues/issue-68615-array.min.stderr | 4 +- .../issues/issue-68615-array.rs | 1 - .../issues/issue-68977.full.stderr | 2 +- .../issues/issue-68977.min.stderr | 4 +- .../ui/const-generics/issues/issue-68977.rs | 1 - .../ui/const-generics/issues/issue-70125-1.rs | 1 - .../ui/const-generics/issues/issue-70125-2.rs | 1 - .../ui/const-generics/issues/issue-70167.rs | 1 - .../issues/issue-71169.full.stderr | 4 +- .../issues/issue-71169.min.stderr | 6 +- .../ui/const-generics/issues/issue-71169.rs | 1 - .../issues/issue-71381.full.stderr | 8 +- .../issues/issue-71381.min.stderr | 8 +- .../ui/const-generics/issues/issue-71381.rs | 1 - .../issues/issue-71382.full.stderr | 2 +- .../issues/issue-71382.min.stderr | 2 +- .../ui/const-generics/issues/issue-71382.rs | 1 - .../issues/issue-71611.full.stderr | 4 +- .../issues/issue-71611.min.stderr | 4 +- .../ui/const-generics/issues/issue-71611.rs | 1 - .../issues/issue-72352.full.stderr | 2 +- .../issues/issue-72352.min.stderr | 2 +- .../ui/const-generics/issues/issue-72352.rs | 1 - .../issues/issue-72787.full.stderr | 10 +- .../issues/issue-72787.min.stderr | 12 +- .../ui/const-generics/issues/issue-72787.rs | 1 - ...ue-72819-generic-in-const-eval.full.stderr | 2 +- ...sue-72819-generic-in-const-eval.min.stderr | 2 +- .../issue-72819-generic-in-const-eval.rs | 1 - .../issues/issue-73491.min.stderr | 4 +- .../ui/const-generics/issues/issue-73491.rs | 1 - .../issues/issue-73508.full.stderr | 2 +- .../issues/issue-73508.min.stderr | 2 +- .../ui/const-generics/issues/issue-73508.rs | 1 - .../issues/issue-74101.min.stderr | 8 +- .../ui/const-generics/issues/issue-74101.rs | 1 - .../issues/issue-74255.min.stderr | 4 +- .../ui/const-generics/issues/issue-74255.rs | 1 - .../issues/issue-74950.min.stderr | 20 +- .../ui/const-generics/issues/issue-74950.rs | 1 - .../issues/issue-75047.min.stderr | 4 +- .../ui/const-generics/issues/issue-75047.rs | 1 - .../ui/const-generics/issues/issue-76595.rs | 2 +- .../const-generics/issues/issue-76595.stderr | 16 +- .../issue-76701-ty-param-in-const.full.stderr | 4 +- .../issue-76701-ty-param-in-const.min.stderr | 4 +- .../issues/issue-76701-ty-param-in-const.rs | 1 - .../ui/const-generics/issues/issue-80062.rs | 10 + .../const-generics/issues/issue-80062.stderr | 11 + .../ui/const-generics/issues/issue-80375.rs | 4 + .../const-generics/issues/issue-80375.stderr | 11 + .../issues/issue70273-assoc-fn.rs | 1 - .../late-bound-vars/in_closure.rs | 18 + .../const-generics/late-bound-vars/simple.rs | 16 + .../macro_rules-braces.full.stderr | 12 +- .../macro_rules-braces.min.stderr | 12 +- .../ui/const-generics/macro_rules-braces.rs | 1 - .../const-generics/min-and-full-same-time.rs | 7 - .../min-and-full-same-time.stderr | 13 - .../min_const_generics/assoc_const.rs | 2 - .../min_const_generics/complex-expression.rs | 2 - .../complex-expression.stderr | 16 +- .../min_const_generics/complex-types.rs | 1 - .../min_const_generics/complex-types.stderr | 28 +- .../const-evaluatable-unchecked.rs | 1 - .../const-evaluatable-unchecked.stderr | 6 +- ...uggest-missing-braces-without-turbofish.rs | 2 - ...st-missing-braces-without-turbofish.stderr | 26 +- ...const-expression-suggest-missing-braces.rs | 2 - ...t-expression-suggest-missing-braces.stderr | 30 +- .../const_fn_in_generics.rs | 2 - .../default_function_param.rs | 4 +- .../default_function_param.stderr | 10 +- .../min_const_generics/default_trait_param.rs | 4 +- .../default_trait_param.stderr | 10 +- .../feature-gate-min_const_generics.rs | 4 - .../feature-gate-min_const_generics.stderr | 12 - .../forbid-non-static-lifetimes.rs | 2 - .../forbid-non-static-lifetimes.stderr | 4 +- .../min_const_generics/invalid-patterns.rs | 1 - .../invalid-patterns.stderr | 16 +- .../min_const_generics/macro-fail.rs | 2 - .../min_const_generics/macro-fail.stderr | 16 +- .../min_const_generics/macro.rs | 2 - .../min_const_generics/self-ty-in-const-1.rs | 2 - .../self-ty-in-const-1.stderr | 6 +- .../min_const_generics/self-ty-in-const-2.rs | 2 - .../self-ty-in-const-2.stderr | 4 +- .../static-reference-array-const-param.rs | 2 - .../static-reference-array-const-param.stderr | 4 +- .../transmute-const-param-static-reference.rs | 2 - ...nsmute-const-param-static-reference.stderr | 4 +- .../mut-ref-const-param-array.rs | 1 - .../ui/const-generics/nested-type.full.stderr | 2 +- .../ui/const-generics/nested-type.min.stderr | 6 +- src/test/ui/const-generics/nested-type.rs | 1 - .../occurs-check/unify-fixpoint.stderr | 1 - ...ms-in-ct-in-ty-param-lazy-norm.full.stderr | 6 +- ...ams-in-ct-in-ty-param-lazy-norm.min.stderr | 6 +- .../params-in-ct-in-ty-param-lazy-norm.rs | 3 +- src/test/ui/const-generics/promotion.rs | 2 - .../raw-ptr-const-param-deref.full.stderr | 4 +- .../raw-ptr-const-param-deref.min.stderr | 4 +- .../raw-ptr-const-param-deref.rs | 1 - .../raw-ptr-const-param.full.stderr | 2 +- .../raw-ptr-const-param.min.stderr | 2 +- .../ui/const-generics/raw-ptr-const-param.rs | 1 - .../slice-const-param-mismatch.full.stderr | 6 +- .../slice-const-param-mismatch.min.stderr | 8 +- .../slice-const-param-mismatch.rs | 1 - .../slice-const-param.min.stderr | 8 +- .../ui/const-generics/slice-const-param.rs | 1 - .../std/const-generics-range.min.stderr | 24 +- .../std/const-generics-range.rs | 1 - ...truct-with-invalid-const-param.full.stderr | 2 +- ...struct-with-invalid-const-param.min.stderr | 2 +- .../struct-with-invalid-const-param.rs | 1 - .../const-generics/suggest_const_for_array.rs | 10 + .../suggest_const_for_array.stderr | 15 + .../ui/const-generics/trait-const-args.rs | 1 - .../transparent-maybeunit-array-wrapper.rs | 1 - .../type-after-const-ok.min.stderr | 2 +- .../ui/const-generics/type-after-const-ok.rs | 1 - .../auxiliary/type_dependent_lib.rs | 1 - .../type-dependent/const-arg-in-const-arg.rs | 1 - .../type-dependent/issue-61936.rs | 1 - .../type-dependent/issue-63695.rs | 1 - .../type-dependent/issue-67144-1.rs | 1 - .../type-dependent/issue-67144-2.rs | 1 - .../type-dependent/issue-69816.rs | 1 - .../type-dependent/issue-70217.rs | 1 - .../type-dependent/issue-70507.rs | 1 - .../type-dependent/issue-70586.rs | 1 - .../type-dependent/issue-71348.min.stderr | 8 +- .../type-dependent/issue-71348.rs | 1 - .../type-dependent/issue-71382.full.stderr | 2 +- .../type-dependent/issue-71382.min.stderr | 2 +- .../type-dependent/issue-71382.rs | 1 - .../type-dependent/issue-71805.rs | 1 - .../type-dependent/issue-73730.rs | 1 - .../type-dependent/non-local.rs | 1 - .../ui/const-generics/type-dependent/qpath.rs | 1 - .../const-generics/type-dependent/simple.rs | 1 - .../type-dependent/type-mismatch.full.stderr | 2 +- .../type-dependent/type-mismatch.min.stderr | 2 +- .../type-dependent/type-mismatch.rs | 1 - .../ui/const-generics/type_of_anon_const.rs | 1 - .../types-mismatch-const-args.full.stderr | 4 +- .../types-mismatch-const-args.min.stderr | 4 +- .../types-mismatch-const-args.rs | 1 - .../uninferred-consts-during-codegen-1.rs | 1 - .../uninferred-consts-during-codegen-2.rs | 1 - .../ui/const-generics/unknown_adt.full.stderr | 2 +- .../ui/const-generics/unknown_adt.min.stderr | 2 +- src/test/ui/const-generics/unknown_adt.rs | 1 - .../ui/const-generics/unused-const-param.rs | 1 - .../const-generics/unused_braces.full.fixed | 1 - .../const-generics/unused_braces.full.stderr | 4 +- .../ui/const-generics/unused_braces.min.fixed | 1 - .../const-generics/unused_braces.min.stderr | 4 +- src/test/ui/const-generics/unused_braces.rs | 1 - .../ui/const-generics/wf-misc.full.stderr | 4 +- src/test/ui/const-generics/wf-misc.min.stderr | 4 +- src/test/ui/const-generics/wf-misc.rs | 1 - src/test/ui/const-generics/where-clauses.rs | 1 - src/test/ui/const-ptr/out_of_bounds_read.rs | 16 + .../ui/const-ptr/out_of_bounds_read.stderr | 62 + .../const_evaluatable/needs_where_clause.rs | 14 + .../needs_where_clause.stderr | 14 + .../ui/const_evaluatable/no_where_clause.rs | 29 + .../const_evaluatable/no_where_clause.stderr | 14 + .../const_prop/ice-assert-fail-div-by-zero.rs | 4 +- .../ice-assert-fail-div-by-zero.stderr | 4 +- .../const_prop/inline_spans_lint_attribute.rs | 15 + src/test/ui/constructor-lifetime-args.rs | 8 +- src/test/ui/constructor-lifetime-args.stderr | 62 +- src/test/ui/consts/array-literal-index-oob.rs | 2 - .../ui/consts/array-literal-index-oob.stderr | 22 +- .../ui/consts/assoc_const_generic_impl.rs | 1 + .../ui/consts/assoc_const_generic_impl.stderr | 4 +- .../ui/consts/assume-type-intrinsics.stderr | 2 + src/test/ui/consts/auxiliary/cci_const.rs | 2 +- src/test/ui/consts/auxiliary/issue-63226.rs | 4 +- .../{ => consts}/check_const-feature-gated.rs | 0 .../const-address-of-interior-mut.stderr | 22 +- .../ui/consts/const-address-of-mut.stderr | 24 +- .../const-repeat.rs | 0 .../fn-call-in-const.rs | 6 +- .../fn-call-in-non-const.rs | 2 - .../fn-call-in-non-const.stderr | 2 +- .../migrate-fail.rs | 1 - .../migrate-fail.stderr | 4 +- .../migrate-pass.rs | 1 - .../nll-fail.rs | 1 - .../nll-fail.stderr | 4 +- .../nll-pass.rs | 1 - .../run-pass.rs | 1 - .../trait-error.rs | 2 - .../trait-error.stderr | 2 +- src/test/ui/consts/const-cast.rs | 2 +- src/test/ui/consts/const-err-early.rs | 5 + src/test/ui/consts/const-err-early.stderr | 22 +- src/test/ui/consts/const-err-multi.rs | 4 + src/test/ui/consts/const-err-multi.stderr | 17 +- src/test/ui/consts/const-err.rs | 1 + src/test/ui/consts/const-err.stderr | 6 +- .../const-eval/conditional_array_execution.rs | 2 + .../conditional_array_execution.stderr | 11 +- .../consts/const-eval/const-eval-overflow2.rs | 8 + .../const-eval/const-eval-overflow2.stderr | 37 +- .../const-eval/const-eval-overflow2b.rs | 8 + .../const-eval/const-eval-overflow2b.stderr | 37 +- .../const-eval/const-eval-overflow2c.rs | 8 + .../const-eval/const-eval-overflow2c.stderr | 37 +- .../const-eval/const-eval-query-stack.rs | 11 +- .../const-eval/const-eval-query-stack.stderr | 34 +- .../const-pointer-values-in-various-types.rs | 20 + ...nst-pointer-values-in-various-types.stderr | 113 +- .../consts/const-eval/const_fn_ptr_fail2.rs | 4 +- .../const-eval/const_fn_ptr_fail2.stderr | 20 +- src/test/ui/consts/const-eval/const_panic.rs | 11 + .../ui/consts/const-eval/const_panic.stderr | 60 +- .../const-eval/const_panic_libcore_bin.rs | 3 + .../const-eval/const_panic_libcore_bin.stderr | 14 +- .../consts/const-eval/const_raw_ptr_ops2.rs | 3 + .../const-eval/const_raw_ptr_ops2.stderr | 12 +- src/test/ui/consts/const-eval/dangling.rs | 6 +- src/test/ui/consts/const-eval/dangling.stderr | 3 + .../ui/consts/const-eval/erroneous-const.rs | 1 + .../consts/const-eval/erroneous-const.stderr | 8 +- .../consts/const-eval/extern_fat_pointer.rs | 2 +- .../feature-gate-const_panic.stderr | 12 +- .../const-eval/heap/alloc_intrinsic_errors.rs | 1 + .../heap/alloc_intrinsic_errors.stderr | 2 + .../index-out-of-bounds-never-type.rs | 1 + .../index-out-of-bounds-never-type.stderr | 4 +- src/test/ui/consts/const-eval/issue-43197.rs | 8 +- .../ui/consts/const-eval/issue-43197.stderr | 25 +- src/test/ui/consts/const-eval/issue-44578.rs | 2 +- .../ui/consts/const-eval/issue-44578.stderr | 2 +- src/test/ui/consts/const-eval/issue-49296.rs | 1 + .../ui/consts/const-eval/issue-49296.stderr | 4 +- .../ui/consts/const-eval/issue-50814-2.rs | 1 + .../ui/consts/const-eval/issue-50814-2.stderr | 10 +- src/test/ui/consts/const-eval/issue-50814.rs | 1 + .../ui/consts/const-eval/issue-50814.stderr | 10 +- .../ui/consts/const-eval/issue-65394.stderr | 9 +- .../const-eval/panic-assoc-never-type.rs | 1 + .../const-eval/panic-assoc-never-type.stderr | 4 +- .../ui/consts/const-eval/panic-never-type.rs | 1 + .../consts/const-eval/panic-never-type.stderr | 4 +- .../const-eval/promoted_errors.noopt.stderr | 111 +- .../const-eval/promoted_errors.opt.stderr | 106 +- ...ted_errors.opt_with_overflow_checks.stderr | 111 +- .../ui/consts/const-eval/promoted_errors.rs | 50 +- .../ui/consts/const-eval/pub_const_err.rs | 1 + .../ui/consts/const-eval/pub_const_err.stderr | 2 + .../ui/consts/const-eval/pub_const_err_bin.rs | 1 + .../const-eval/pub_const_err_bin.stderr | 2 + src/test/ui/consts/const-eval/ub-nonnull.rs | 1 + .../ui/consts/const-eval/ub-nonnull.stderr | 17 +- src/test/ui/consts/const-eval/ub-wide-ptr.rs | 14 +- .../ui/consts/const-eval/ub-wide-ptr.stderr | 72 +- .../consts/const-eval/unused-broken-const.rs | 1 + .../const-eval/unused-broken-const.stderr | 2 + src/test/ui/consts/const-eval/unwind-abort.rs | 1 + .../ui/consts/const-eval/unwind-abort.stderr | 6 +- .../const-eval/validate_uninhabited_zsts.rs | 1 + .../validate_uninhabited_zsts.stderr | 10 +- .../const-extern-fn-call-extern-fn.rs | 6 +- .../const-extern-fn-min-const-fn.rs | 4 +- .../const-extern-fn-min-const-fn.stderr | 6 +- .../const-extern-fn-requires-unsafe.rs | 2 +- .../consts/const-extern-fn/const-extern-fn.rs | 12 +- src/test/ui/consts/const-extern-function.rs | 2 +- .../consts/const-external-macro-const-err.rs | 1 + .../const-external-macro-const-err.stderr | 2 + .../consts/const-fn-error.rs | 0 src/test/ui/consts/const-fn-error.stderr | 49 + src/test/ui/consts/const-int-unchecked.rs | 47 + src/test/ui/consts/const-int-unchecked.stderr | 232 +- .../const-len-underflow-separate-spans.rs | 1 + .../const-len-underflow-separate-spans.stderr | 4 +- src/test/ui/consts/const-multi-ref.rs | 2 +- src/test/ui/consts/const-multi-ref.stderr | 15 +- .../const-mut-refs/const_mut_address_of.rs | 3 +- .../const_mut_address_of.stderr | 15 - .../consts/const-mut-refs/const_mut_refs.rs | 4 +- .../const-mut-refs/const_mut_refs.stderr | 21 - .../consts/const-mut-refs/mut_ref_in_final.rs | 57 + .../const-mut-refs/mut_ref_in_final.stderr | 60 + .../mut_ref_in_final_dynamic_check.rs | 29 + .../mut_ref_in_final_dynamic_check.stderr | 25 + .../consts/const-prop-read-static-in-const.rs | 1 + .../const-prop-read-static-in-const.stderr | 2 + ...st-size_of_val-align_of_val-extern-type.rs | 6 +- ...ize_of_val-align_of_val-extern-type.stderr | 7 +- .../consts/const-size_of_val-align_of_val.rs | 7 + src/test/ui/consts/const-slice-oob.rs | 1 + src/test/ui/consts/const-slice-oob.stderr | 2 + .../ui/{ => consts}/const-suggest-feature.rs | 0 .../{ => consts}/const-suggest-feature.stderr | 0 src/test/ui/consts/const-unwrap.stderr | 4 +- src/test/ui/consts/const_let_assign3.stderr | 17 +- .../const_limit/const_eval_limit_reached.rs | 1 + .../const_eval_limit_reached.stderr | 3 + .../ui/consts/const_unsafe_unreachable_ub.rs | 1 + .../consts/const_unsafe_unreachable_ub.stderr | 14 +- .../control-flow/assert.const_panic.stderr | 2 + src/test/ui/consts/control-flow/assert.rs | 1 + .../ui/consts/intrinsic_without_const_stab.rs | 17 + .../intrinsic_without_const_stab.stderr | 9 + .../intrinsic_without_const_stab_fail.rs | 15 + .../intrinsic_without_const_stab_fail.stderr | 9 + src/test/ui/{issues => consts}/issue-17458.rs | 0 .../ui/{issues => consts}/issue-17458.stderr | 0 .../issue-17718-borrow-interior.rs | 0 .../issue-17718-const-bad-values.rs | 3 +- .../issue-17718-const-bad-values.stderr | 12 +- .../issue-17718-const-borrow.rs | 6 +- .../ui/consts/issue-17718-const-borrow.stderr | 21 + src/test/ui/{issues => consts}/issue-21562.rs | 0 src/test/ui/{issues => consts}/issue-21721.rs | 0 src/test/ui/{issues => consts}/issue-25826.rs | 0 .../ui/{issues => consts}/issue-25826.stderr | 0 src/test/ui/{issues => consts}/issue-27890.rs | 0 src/test/ui/{issues => consts}/issue-28113.rs | 0 .../ui/{issues => consts}/issue-28113.stderr | 0 .../ui/{issues => consts}/issue-29914-2.rs | 0 .../ui/{issues => consts}/issue-29914-3.rs | 0 src/test/ui/{issues => consts}/issue-29914.rs | 0 .../ui/{issues => consts}/issue-29927-1.rs | 0 src/test/ui/{issues => consts}/issue-29927.rs | 0 .../ui/{issues => consts}/issue-32829-2.rs | 0 .../{issues => consts}/issue-32829-2.stderr | 0 src/test/ui/{issues => consts}/issue-36163.rs | 0 .../ui/{issues => consts}/issue-36163.stderr | 0 src/test/ui/{issues => consts}/issue-43105.rs | 0 .../ui/{issues => consts}/issue-43105.stderr | 0 .../consts}/issue-44415.rs | 0 src/test/ui/consts/issue-44415.stderr | 28 + src/test/ui/{issues => consts}/issue-46553.rs | 0 src/test/ui/{issues => consts}/issue-47789.rs | 0 src/test/ui/consts/issue-51559.rs | 1 + src/test/ui/consts/issue-51559.stderr | 2 + .../issue-52023-array-size-pointer-cast.rs | 0 ...issue-52023-array-size-pointer-cast.stderr | 0 src/test/ui/{issues => consts}/issue-54348.rs | 0 .../ui/{issues => consts}/issue-54348.stderr | 0 .../consts/issue-55878.rs | 1 + src/test/ui/consts/issue-55878.stderr | 24 + src/test/ui/{issues => consts}/issue-56762.rs | 0 .../ui/{issues => consts}/issue-56762.stderr | 0 .../issue-58435-ice-with-assoc-const.rs | 0 src/test/ui/{issues => consts}/issue-6991.rs | 0 src/test/ui/consts/issue-79690.rs | 32 + src/test/ui/consts/issue-79690.stderr | 11 + .../allow_raw_ptr_dereference_const_fn.rs | 3 +- .../const_refers_to_static_cross_crate.rs | 5 + .../const_refers_to_static_cross_crate.stderr | 42 +- src/test/ui/consts/offset_from_ub.stderr | 14 + src/test/ui/consts/offset_ub.stderr | 32 + src/test/ui/consts/partial_qualif.rs | 2 +- src/test/ui/consts/partial_qualif.stderr | 4 +- .../consts/projection_qualif.mut_refs.stderr | 19 - .../ui/consts/projection_qualif.stock.stderr | 19 - src/test/ui/consts/promote-not.rs | 7 + src/test/ui/consts/promote-not.stderr | 59 +- src/test/ui/consts/promoted_div_by_zero.rs | 9 - src/test/ui/consts/promotion.rs | 46 +- src/test/ui/consts/ptr_comparisons.rs | 12 +- src/test/ui/consts/ptr_comparisons.stderr | 29 +- src/test/ui/consts/qualif_overwrite.rs | 2 +- src/test/ui/consts/qualif_overwrite.stderr | 4 +- src/test/ui/consts/qualif_overwrite_2.rs | 2 +- src/test/ui/consts/qualif_overwrite_2.stderr | 4 +- .../ui/consts/read_from_static_mut_ref.rs | 9 - .../ui/consts/read_from_static_mut_ref.stderr | 9 - .../{ => consts}/rustc-args-required-const.rs | 0 .../rustc-args-required-const.stderr | 0 ...ic_mut_containing_mut_ref2.mut_refs.stderr | 8 +- .../consts/static_mut_containing_mut_ref2.rs | 3 +- ...tatic_mut_containing_mut_ref2.stock.stderr | 9 +- src/test/ui/consts/std/cell.rs | 30 +- src/test/ui/consts/std/cell.stderr | 27 +- .../transmute-size-mismatch-before-typeck.rs | 1 + ...ansmute-size-mismatch-before-typeck.stderr | 2 + .../consts/uninhabited-const-issue-61744.rs | 1 + .../uninhabited-const-issue-61744.stderr | 132 +- .../write_to_mut_ref_dest.mut_refs.stderr | 12 + ...ion_qualif.rs => write_to_mut_ref_dest.rs} | 2 +- .../consts/write_to_mut_ref_dest.stock.stderr | 21 + .../ui/consts/write_to_static_via_mut_ref.rs | 8 + .../consts/write_to_static_via_mut_ref.stderr | 16 + .../auxiliary/crateresolve1-1.rs | 0 .../auxiliary/crateresolve1-2.rs | 0 .../auxiliary/crateresolve1-3.rs | 0 .../crate-loading}/crateresolve1.rs | 1 + .../ui/cross-crate/auxiliary/cci_const.rs | 2 +- .../derives/derive-Debug-use-ufcs-struct.rs | 40 + .../ui/derives/derive-Debug-use-ufcs-tuple.rs | 32 + .../derives/derive-assoc-type-not-impl.stderr | 6 +- .../struct_destructure_fail.stderr | 8 +- .../tuple_struct_destructure_fail.stderr | 18 + src/test/ui/doc-alias-crate-level.rs | 2 - src/test/ui/doc-alias-crate-level.stderr | 4 +- src/test/ui/doc-alias-same-name.rs | 4 + src/test/ui/doc-alias-same-name.stderr | 8 + .../{issues => drop}/auxiliary/issue-10028.rs | 0 src/test/ui/drop/dynamic-drop-async.rs | 2 +- src/test/ui/drop/dynamic-drop.rs | 4 +- src/test/ui/{issues => drop}/issue-10028.rs | 0 .../{issues => drop}/issue-30018-nopanic.rs | 0 .../ui/dropck/dropck_trait_cycle_checked.rs | 2 +- .../issue-28498-ugeh-with-lifetime-param.rs | 2 +- .../issue-28498-ugeh-with-trait-bound.rs | 2 +- src/test/ui/{issues => dropck}/issue-29844.rs | 0 .../dropck/reject-specialized-drops-8142.rs | 2 - .../reject-specialized-drops-8142.stderr | 62 +- src/test/ui/duplicate/dupe-symbols-2.rs | 4 +- src/test/ui/duplicate/dupe-symbols-2.stderr | 4 +- src/test/ui/editions/async-block-2015.rs | 10 +- src/test/ui/editions/async-block-2015.stderr | 10 +- .../edition-imports-virtual-2015-gated.stderr | 2 +- src/test/ui/emit-metadata-obj.rs | 7 + src/test/ui/empty/empty-linkname.rs | 3 +- .../issue-43398.rs | 0 .../issue-43398.stderr | 0 .../issue-70453-generics-in-discr-ice-2.rs | 2 +- ...issue-70453-generics-in-discr-ice-2.stderr | 9 +- .../issue-70453-generics-in-discr-ice.rs | 2 +- .../issue-70453-generics-in-discr-ice.stderr | 9 +- .../issue-70453-polymorphic-ctfe.rs | 4 +- .../issue-70453-polymorphic-ctfe.stderr | 13 +- src/test/ui/enum/issue-67945-1.rs | 4 +- src/test/ui/enum/issue-67945-1.stderr | 27 +- src/test/ui/enum/issue-67945-2.rs | 5 +- src/test/ui/enum/issue-67945-2.stderr | 25 +- src/test/ui/error-codes/E0017.rs | 9 +- src/test/ui/error-codes/E0017.stderr | 35 +- src/test/ui/error-codes/E0023.stderr | 9 +- src/test/ui/error-codes/E0027.rs | 9 + src/test/ui/error-codes/E0027.stderr | 40 +- src/test/ui/error-codes/E0040.fixed | 18 + src/test/ui/error-codes/E0040.rs | 3 + src/test/ui/error-codes/E0040.stderr | 10 +- src/test/ui/error-codes/E0044.rs | 11 +- src/test/ui/error-codes/E0107.rs | 15 +- src/test/ui/error-codes/E0107.stderr | 46 +- src/test/ui/error-codes/E0130.rs | 7 +- src/test/ui/error-codes/E0283.rs | 18 + src/test/ui/error-codes/E0283.stderr | 16 +- src/test/ui/error-codes/E0388.rs | 7 +- src/test/ui/error-codes/E0388.stderr | 29 +- src/test/ui/error-codes/E0396-fixed.rs | 1 + src/test/ui/error-codes/E0396-fixed.stderr | 2 + src/test/ui/error-codes/E0435.fixed | 6 + src/test/ui/error-codes/E0435.rs | 4 +- src/test/ui/error-codes/E0435.stderr | 4 +- src/test/ui/error-codes/E0454.rs | 2 +- src/test/ui/error-codes/E0454.stderr | 2 +- src/test/ui/error-codes/E0458.rs | 4 +- src/test/ui/error-codes/E0458.stderr | 4 +- src/test/ui/error-codes/E0459.rs | 2 +- src/test/ui/error-codes/E0459.stderr | 2 +- src/test/ui/error-codes/E0492.rs | 5 +- src/test/ui/error-codes/E0492.stderr | 18 +- src/test/ui/error-codes/E0617.rs | 2 +- src/test/ui/error-codes/E0730.stderr | 1 - src/test/ui/error-codes/E0771.stderr | 1 - .../ui/explicit/explicit-call-to-dtor.fixed | 16 + src/test/ui/explicit/explicit-call-to-dtor.rs | 2 + .../ui/explicit/explicit-call-to-dtor.stderr | 10 +- .../explicit-call-to-supertrait-dtor.fixed | 26 + .../explicit-call-to-supertrait-dtor.rs | 5 +- .../explicit-call-to-supertrait-dtor.stderr | 10 +- .../extern-flag}/empty-extern-arg.rs | 0 .../ui/extern-flag/empty-extern-arg.stderr | 4 + .../ui/extern/auxiliary/extern-take-value.rs | 8 +- .../auxiliary/extern_calling_convention.rs | 2 +- src/test/ui/extern/extern-1.rs | 2 +- .../extern/extern-compare-with-return-type.rs | 22 +- src/test/ui/extern/extern-main-fn.rs | 2 +- src/test/ui/extern/extern-main-fn.stderr | 4 +- src/test/ui/extern/extern-methods.rs | 4 +- src/test/ui/extern/extern-pub.rs | 5 +- src/test/ui/extern/extern-rust.rs | 2 +- .../ui/extern/extern-types-distinct-types.rs | 4 +- .../extern/extern-types-manual-sync-send.rs | 10 +- .../ui/extern/extern-types-not-sync-send.rs | 6 +- .../extern/extern-types-not-sync-send.stderr | 4 +- .../ui/extern/extern-types-pointer-cast.rs | 3 +- .../ui/extern/extern-types-size_of_val.rs | 8 +- .../ui/extern/extern-types-thin-pointer.rs | 3 +- src/test/ui/extern/extern-types-trait-impl.rs | 9 +- src/test/ui/extern/extern-types-unsized.rs | 4 +- .../ui/extern/extern-types-unsized.stderr | 16 +- src/test/ui/extern/extern-wrong-value-type.rs | 2 +- src/test/ui/{issues => extern}/issue-10025.rs | 2 +- src/test/ui/{issues => extern}/issue-10763.rs | 0 .../{issues => extern}/issue-10764-rpass.rs | 0 .../issue-36122-accessing-externed-dst.rs | 2 +- .../allow-features-empty.rs | 0 .../allow-features-empty.stderr | 0 .../allow-features.rs | 0 .../allow-features.stderr | 0 .../duplicate-features.rs | 0 .../duplicate-features.stderr | 0 .../feature-gate-c_variadic.rs | 0 .../feature-gate-c_variadic.stderr | 0 .../feature-gate-cfg-target-thread-local.rs | 3 +- .../feature-gates/feature-gate-cfg-version.rs | 30 +- .../feature-gate-cfg-version.stderr | 110 +- .../feature-gate-const_generics-ptr.rs | 9 - .../feature-gate-const_generics-ptr.stderr | 33 - .../feature-gate-const_generics.rs | 4 +- .../feature-gate-const_generics.stderr | 22 +- .../feature-gate-const_generics_defaults.rs | 9 + ...eature-gate-const_generics_defaults.stderr | 21 + ...-gate-const_in_array_repeat_expressions.rs | 17 - ...e-const_in_array_repeat_expressions.stderr | 25 - .../feature-gate-const_refs_to_cell.rs | 12 + .../feature-gate-edition_macro_pats.rs | 8 + .../feature-gate-edition_macro_pats.stderr | 21 + .../feature-gate-extern_types.rs | 2 +- .../feature-gates/feature-gate-ffi_const.rs | 2 +- .../ui/feature-gates/feature-gate-ffi_pure.rs | 2 +- .../feature-gate-ffi_returns_twice.rs | 2 +- .../feature-gate-inline_const.rs | 0 .../feature-gate-inline_const.stderr | 0 .../feature-gate-isa_attribute.rs | 0 .../feature-gate-isa_attribute.stderr | 0 .../feature-gates/feature-gate-link_args.rs | 3 +- .../feature-gate-link_args.stderr | 6 +- .../ui/feature-gates/feature-gate-link_cfg.rs | 2 +- .../feature-gate-link_llvm_intrinsics.rs | 7 +- .../ui/feature-gates/feature-gate-linkage.rs | 2 +- .../feature-gate-non_ascii_idents.rs | 2 +- .../feature-gate-optimize_attribute.rs | 0 .../feature-gate-optimize_attribute.stderr | 0 .../feature-gate-relaxed_struct_unsize.rs | 10 + .../feature-gate-relaxed_struct_unsize.stderr | 14 + .../ui/feature-gates/feature-gate-simd-ffi.rs | 2 +- .../feature-gate-static-nobundle-2.rs | 0 .../feature-gate-static-nobundle-2.stderr | 0 .../feature-gate-static-nobundle.rs | 4 +- .../feature-gate-static-nobundle.stderr | 4 +- .../feature-gate-unwind-attributes.rs | 10 +- .../feature-gated-feature-in-macro-arg.rs | 0 .../feature-gated-feature-in-macro-arg.stderr | 0 .../issue-43106-gating-of-bench.rs | 0 .../issue-43106-gating-of-bench.stderr | 0 ...sue-43106-gating-of-builtin-attrs-error.rs | 0 ...43106-gating-of-builtin-attrs-error.stderr | 0 .../issue-43106-gating-of-builtin-attrs.rs | 2 +- ...issue-43106-gating-of-builtin-attrs.stderr | 16 +- .../issue-43106-gating-of-deprecated.rs | 0 .../issue-43106-gating-of-derive-2.rs | 0 .../issue-43106-gating-of-derive-2.stderr | 0 .../issue-43106-gating-of-derive.rs | 0 .../issue-43106-gating-of-derive.stderr | 0 .../issue-43106-gating-of-macro_escape.rs | 0 .../issue-43106-gating-of-macro_escape.stderr | 0 .../issue-43106-gating-of-macro_use.rs | 0 .../issue-43106-gating-of-macro_use.stderr | 12 +- ...issue-43106-gating-of-proc_macro_derive.rs | 0 ...e-43106-gating-of-proc_macro_derive.stderr | 0 .../issue-43106-gating-of-rustc_deprecated.rs | 0 ...ue-43106-gating-of-rustc_deprecated.stderr | 0 .../issue-43106-gating-of-stable.rs | 0 .../issue-43106-gating-of-stable.stderr | 0 .../issue-43106-gating-of-test.rs | 0 .../issue-43106-gating-of-test.stderr | 0 .../issue-43106-gating-of-unstable.rs | 0 .../issue-43106-gating-of-unstable.stderr | 0 .../issue-49983-see-issue-0.rs | 0 .../issue-49983-see-issue-0.stderr | 0 .../rustc-private.rs | 0 .../rustc-private.stderr | 0 .../stability-attribute-consistency.rs | 0 .../stability-attribute-consistency.stderr | 0 .../{ => feature-gates}/trace_macros-gate.rs | 0 .../trace_macros-gate.stderr | 0 .../unknown-feature.rs | 0 .../unknown-feature.stderr | 0 .../unstable-attribute-allow-issue-0.rs | 0 .../unstable-attribute-allow-issue-0.stderr | 0 src/test/ui/ffi_const2.rs | 2 +- src/test/ui/fn/issue-80179.rs | 27 + src/test/ui/fn/issue-80179.stderr | 21 + .../{issues => for-loop-while}/issue-2216.rs | 0 .../{issues => for-loop-while}/issue-69841.rs | 0 src/test/ui/foreign-unsafe-fn-called.rs | 2 +- src/test/ui/foreign/foreign-fn-linkname.rs | 6 +- src/test/ui/foreign/foreign-int-types.rs | 5 +- .../ui/foreign/foreign-mod-unused-const.rs | 5 +- src/test/ui/foreign/foreign2.rs | 11 +- src/test/ui/fsu-moves-and-copies.rs | 2 +- .../ui/functions-closures/auxiliary/fn-abi.rs | 2 +- .../closure-expected-type/README.md | 2 +- src/test/ui/functions-closures/fn-abi.rs | 4 +- .../metadata-sufficient-for-layout.rs | 11 + src/test/ui/generator/layout-error.rs | 28 + src/test/ui/generator/layout-error.stderr | 9 + .../metadata-sufficient-for-layout.rs | 23 + .../ref-escapes-but-not-over-yield.stderr | 1 + .../ui/generator/resume-arg-late-bound.stderr | 32 +- .../gat-in-trait-path-undeclared-lifetime.rs | 12 + ...t-in-trait-path-undeclared-lifetime.stderr | 29 + .../gat-in-trait-path.rs | 30 + .../gat-in-trait-path.stderr | 11 + .../gat-trait-path-generic-type-arg.rs | 16 + .../gat-trait-path-generic-type-arg.stderr | 32 + .../gat-trait-path-missing-lifetime.rs | 18 + .../gat-trait-path-missing-lifetime.stderr | 44 + .../gat-trait-path-parenthesised-args.rs | 15 + .../gat-trait-path-parenthesised-args.stderr | 68 + .../issue-67510-pass.rs | 12 + .../issue-67510-pass.stderr | 11 + .../generic-associated-types/issue-67510.rs | 13 + .../issue-67510.stderr | 32 + .../generic-associated-types/issue-68648-1.rs | 26 + .../issue-68648-1.stderr | 11 + .../generic-associated-types/issue-68648-2.rs | 24 + .../issue-68648-2.stderr | 23 + .../issue-68649-pass.rs | 25 + .../issue-68649-pass.stderr | 11 + .../generic-associated-types/issue-74684-1.rs | 26 + .../issue-74684-1.stderr | 27 + .../generic-associated-types/issue-74684-2.rs | 26 + .../issue-74684-2.stderr | 21 + .../generic-associated-types/issue-76535.rs | 41 + .../issue-76535.stderr | 63 + .../generic-associated-types/issue-79422.rs | 47 + .../issue-79422.stderr | 54 + .../issue-80433-reduced.rs | 24 + .../generic-associated-types/issue-80433.rs | 35 + .../issue-80433.stderr | 19 + .../parameter_number_and_kind.rs | 6 +- .../parameter_number_and_kind.stderr | 44 +- .../parse/trait-path-expected-token.stderr | 4 +- .../parse/trait-path-expressions.rs | 2 +- .../parse/trait-path-expressions.stderr | 8 +- .../parse/trait-path-missing-gen_arg.stderr | 4 +- .../parse/trait-path-segments.rs | 6 +- .../parse/trait-path-segments.stderr | 26 +- .../trait-path-type-error-once-implemented.rs | 6 +- ...it-path-type-error-once-implemented.stderr | 51 +- .../parse/trait-path-types.rs | 6 +- .../parse/trait-path-types.stderr | 24 +- .../parse/trait-path-unimplemented.rs | 17 - .../parse/trait-path-unimplemented.stderr | 14 - .../variance_constraints.rs | 24 + .../generics/generic-arg-mismatch-recover.rs | 8 +- .../generic-arg-mismatch-recover.stderr | 42 +- .../ui/generics/generic-extern-lifetime.rs | 20 +- .../generics/generic-extern-lifetime.stderr | 26 +- src/test/ui/generics/generic-extern.rs | 2 +- .../generic-impl-less-params-with-defaults.rs | 2 +- ...eric-impl-less-params-with-defaults.stderr | 16 +- .../generic-impl-more-params-with-defaults.rs | 2 +- ...eric-impl-more-params-with-defaults.stderr | 14 +- src/test/ui/generics/generic-no-mangle.fixed | 2 +- src/test/ui/generics/generic-no-mangle.rs | 2 +- src/test/ui/generics/generic-no-mangle.stderr | 4 +- .../generic-type-less-params-with-defaults.rs | 2 +- ...eric-type-less-params-with-defaults.stderr | 12 +- .../generic-type-more-params-with-defaults.rs | 2 +- ...eric-type-more-params-with-defaults.stderr | 14 +- .../ui/{issues => generics}/issue-2936.rs | 0 .../param-in-ct-in-ty-param-default.rs | 2 +- .../param-in-ct-in-ty-param-default.stderr | 7 +- src/test/ui/generics/wrong-number-of-args.rs | 161 + .../ui/generics/wrong-number-of-args.stderr | 449 + src/test/ui/hashmap/hashmap-memory.rs | 2 +- src/test/ui/hello2021.rs | 7 + .../ui/hrtb/hrtb-perfect-forwarding.stderr | 24 +- src/test/ui/hrtb/issue-30786.migrate.stderr | 12 +- src/test/ui/hrtb/issue-30786.nll.stderr | 12 +- src/test/ui/hrtb/issue-30786.rs | 8 +- src/test/ui/hygiene/generic_params.stderr | 1 - .../issue-61574-const-parameters.stderr | 1 - .../ui/hygiene/no_implicit_prelude-2021.rs | 9 + src/test/ui/hygiene/traits-in-scope.rs | 53 + src/test/ui/illegal-ufcs-drop.fixed | 10 + src/test/ui/illegal-ufcs-drop.rs | 1 + src/test/ui/illegal-ufcs-drop.stderr | 2 +- .../dyn-trait.nll.stderr | 1 + src/test/ui/impl-trait/bindings.stderr | 16 +- .../ui/{ => impl-trait}/nested_impl_trait.rs | 0 .../{ => impl-trait}/nested_impl_trait.stderr | 0 ...-to-type-err-cause-on-impl-trait-return.rs | 0 ...type-err-cause-on-impl-trait-return.stderr | 0 src/test/ui/imports/glob-resolve1.rs | 10 +- src/test/ui/imports/glob-resolve1.stderr | 30 +- .../issue-26873-onefile.rs | 0 .../ui/{issues => imports}/issue-32119.rs | 0 .../ui/{issues => imports}/issue-32222.rs | 0 ...r-async-enabled-impl-trait-bindings.stderr | 4 +- .../ui/inference/cannot-infer-async.stderr | 4 +- .../cannot-infer-closure-circular.rs | 14 + .../cannot-infer-closure-circular.stderr | 9 + .../ui/inference/cannot-infer-closure.stderr | 3 +- .../cannot-infer-partial-try-return.rs | 22 + .../cannot-infer-partial-try-return.stderr | 15 + src/test/ui/internal/internal-unstable.rs | 13 + src/test/ui/internal/internal-unstable.stderr | 10 +- .../intrinsics/intrinsic-move-val-cleanups.rs | 191 - src/test/ui/intrinsics/intrinsic-move-val.rs | 81 - .../ui/issues/auxiliary/issue-10031-aux.rs | 1 - src/test/ui/issues/auxiliary/issue-13620-1.rs | 4 +- src/test/ui/issues/auxiliary/issue-15562.rs | 2 +- src/test/ui/issues/auxiliary/issue-16725.rs | 2 +- src/test/ui/issues/auxiliary/issue-25185-1.rs | 2 +- src/test/ui/issues/issue-10031.rs | 9 - src/test/ui/issues/issue-10764.rs | 2 +- src/test/ui/issues/issue-10877.rs | 8 +- src/test/ui/issues/issue-10877.stderr | 12 +- src/test/ui/issues/issue-1251.rs | 5 +- src/test/ui/issues/issue-13033.stderr | 9 +- src/test/ui/issues/issue-14091.stderr | 2 + src/test/ui/issues/issue-14092.rs | 2 +- src/test/ui/issues/issue-14092.stderr | 14 +- src/test/ui/issues/issue-14227.rs | 2 +- src/test/ui/issues/issue-15487.rs | 9 +- src/test/ui/issues/issue-15562.rs | 2 +- .../issue-15881-model-lexer-dotdotdot.rs | 38 - src/test/ui/issues/issue-16149.rs | 4 +- src/test/ui/issues/issue-16250.rs | 5 +- src/test/ui/issues/issue-16538.rs | 2 +- src/test/ui/issues/issue-16683.nll.stderr | 1 + src/test/ui/issues/issue-16939.stderr | 6 + .../ui/issues/issue-17718-const-borrow.stderr | 21 - src/test/ui/issues/issue-17758.nll.stderr | 1 + src/test/ui/issues/issue-18423.rs | 6 +- src/test/ui/issues/issue-18423.stderr | 14 +- src/test/ui/issues/issue-1866.rs | 8 +- .../ui/issues/issue-18804/auxiliary/lib.rs | 6 +- src/test/ui/issues/issue-1962.fixed | 4 +- src/test/ui/issues/issue-1962.rs | 4 +- src/test/ui/issues/issue-1962.stderr | 4 +- src/test/ui/issues/issue-20313-rpass.rs | 6 +- src/test/ui/issues/issue-20313.rs | 5 +- src/test/ui/issues/issue-20433.stderr | 2 +- src/test/ui/issues/issue-21596.stderr | 6 +- src/test/ui/issues/issue-2214.rs | 18 +- src/test/ui/issues/issue-23024.rs | 2 +- src/test/ui/issues/issue-23024.stderr | 12 +- src/test/ui/issues/issue-26217.stderr | 2 - src/test/ui/issues/issue-26996.rs | 2 +- src/test/ui/issues/issue-26997.rs | 2 +- src/test/ui/issues/issue-27021.rs | 2 +- src/test/ui/issues/issue-27042.stderr | 2 +- src/test/ui/issues/issue-27433.fixed | 7 + src/test/ui/issues/issue-27433.rs | 2 + src/test/ui/issues/issue-27433.stderr | 6 +- src/test/ui/issues/issue-28324.rs | 2 +- src/test/ui/issues/issue-28472.rs | 2 +- .../issue-28498-ugeh-with-passed-to-fn.rs | 2 +- src/test/ui/issues/issue-28600.rs | 2 +- src/test/ui/issues/issue-3044.stderr | 6 + src/test/ui/issues/issue-31173.rs | 2 +- src/test/ui/issues/issue-31173.stderr | 6 +- src/test/ui/issues/issue-3214.rs | 2 +- src/test/ui/issues/issue-3214.stderr | 14 +- src/test/ui/issues/issue-32201.rs | 2 +- src/test/ui/issues/issue-34334.rs | 4 +- src/test/ui/issues/issue-34334.stderr | 11 +- src/test/ui/issues/issue-34721.stderr | 2 +- src/test/ui/issues/issue-3521-2.fixed | 9 + src/test/ui/issues/issue-3521-2.rs | 1 + src/test/ui/issues/issue-3521-2.stderr | 6 +- src/test/ui/issues/issue-3521.fixed | 13 + src/test/ui/issues/issue-3521.rs | 4 +- src/test/ui/issues/issue-3521.stderr | 5 +- src/test/ui/issues/issue-35677.rs | 2 +- src/test/ui/issues/issue-35677.stderr | 6 +- src/test/ui/issues/issue-3656.rs | 7 +- src/test/ui/issues/issue-3668-2.fixed | 8 + src/test/ui/issues/issue-3668-2.rs | 2 + src/test/ui/issues/issue-3668-2.stderr | 6 +- src/test/ui/issues/issue-3668.stderr | 4 +- src/test/ui/issues/issue-39559.rs | 2 +- src/test/ui/issues/issue-39559.stderr | 10 +- src/test/ui/issues/issue-42060.stderr | 4 + src/test/ui/issues/issue-43424.stderr | 4 +- src/test/ui/issues/issue-43925.rs | 4 +- src/test/ui/issues/issue-43925.stderr | 6 +- src/test/ui/issues/issue-43926.rs | 4 +- src/test/ui/issues/issue-43926.stderr | 6 +- src/test/ui/issues/issue-44239.fixed | 11 + src/test/ui/issues/issue-44239.rs | 4 +- src/test/ui/issues/issue-44239.stderr | 5 +- src/test/ui/issues/issue-46604.rs | 2 +- src/test/ui/issues/issue-46604.stderr | 4 +- src/test/ui/issues/issue-49298.rs | 2 +- .../option-as_deref.rs | 2 +- .../option-as_deref.stderr | 6 +- .../option-as_deref_mut.rs | 2 +- .../option-as_deref_mut.stderr | 6 +- .../result-as_deref.rs | 2 +- .../result-as_deref.stderr | 6 +- .../result-as_deref_mut.rs | 2 +- .../result-as_deref_mut.stderr | 6 +- src/test/ui/issues/issue-51907.rs | 8 +- src/test/ui/issues/issue-53251.rs | 8 +- src/test/ui/issues/issue-53251.stderr | 26 +- src/test/ui/issues/issue-57362-2.rs | 2 +- src/test/ui/issues/issue-57362-2.stderr | 6 +- src/test/ui/issues/issue-57843.stderr | 5 + src/test/ui/issues/issue-5791.rs | 4 +- src/test/ui/issues/issue-59508-1.stderr | 1 - src/test/ui/issues/issue-59508.stderr | 2 +- src/test/ui/issues/issue-60622.rs | 4 +- src/test/ui/issues/issue-60622.stderr | 14 +- src/test/ui/issues/issue-61108.stderr | 2 +- src/test/ui/issues/issue-64559.stderr | 2 +- src/test/ui/issues/issue-6470.rs | 5 +- src/test/ui/issues/issue-69725.rs | 2 +- src/test/ui/issues/issue-69725.stderr | 6 +- src/test/ui/issues/issue-72574-2.stderr | 5 + ...ue-80512-param-reordering-with-defaults.rs | 4 + ...0512-param-reordering-with-defaults.stderr | 8 + src/test/ui/issues/issue-80607.rs | 10 + src/test/ui/issues/issue-80607.stderr | 14 + src/test/ui/label/label_misspelled.rs | 50 +- src/test/ui/label/label_misspelled.stderr | 211 +- src/test/ui/label/label_misspelled_2.rs | 16 + src/test/ui/label/label_misspelled_2.stderr | 37 + .../feature-gate-lazy_normalization_consts.rs | 2 +- ...ture-gate-lazy_normalization_consts.stderr | 16 +- .../ui/lifetime-before-type-params.stderr | 8 +- .../ui/lifetimes/issue-79187-2.nll.stderr | 44 + src/test/ui/lifetimes/issue-79187-2.rs | 23 + src/test/ui/lifetimes/issue-79187-2.stderr | 60 + src/test/ui/lifetimes/issue-79187.nll.stderr | 14 + src/test/ui/lifetimes/issue-79187.rs | 6 + src/test/ui/lifetimes/issue-79187.stderr | 22 + ...etime-bound-will-change-warning.nll.stderr | 1 + .../liveness-assign-imm-local-notes.stderr | 6 +- src/test/ui/link-cfg-works.rs | 4 +- .../auxiliary/def_colliding_external.rs | 4 +- .../linkage-attr}/invalid-link-args.rs | 6 +- .../linkage-attr}/issue-10755.rs | 2 + ...e-detect-local-generated-name-collision.rs | 6 +- src/test/ui/linkage-attr/linkage2.rs | 7 +- src/test/ui/linkage-attr/linkage2.stderr | 6 +- src/test/ui/linkage-attr/linkage3.rs | 7 +- src/test/ui/linkage-attr/linkage3.stderr | 6 +- src/test/ui/linkage1.rs | 2 +- src/test/ui/lint/clashing-extern-fn-wasm.rs | 21 + src/test/ui/lint/dead-code/const-and-self.rs | 21 +- .../ui/lint/dead-code/const-and-self.stderr | 20 + .../ui/lint/dead-code/leading-underscore.rs | 4 +- .../ui/lint/dead-code/lint-dead-code-3.rs | 6 +- .../ui/{ => lint}/expr_attr_paren_order.rs | 0 .../{ => lint}/expr_attr_paren_order.stderr | 0 src/test/ui/lint/function-item-references.rs | 2 +- .../ui/lint/inline-trait-and-foreign-items.rs | 2 +- src/test/ui/{issues => lint}/issue-14309.rs | 0 .../ui/{issues => lint}/issue-14309.stderr | 0 .../issue-17718-const-naming.rs | 0 .../issue-17718-const-naming.stderr | 0 src/test/ui/{issues => lint}/issue-30302.rs | 0 .../ui/{issues => lint}/issue-30302.stderr | 0 src/test/ui/{issues => lint}/issue-34798.rs | 0 .../ui/lint/lint-const-item-mutation.stderr | 14 +- src/test/ui/lint/lint-ctypes-enum.rs | 41 +- src/test/ui/lint/lint-ctypes-enum.stderr | 45 +- src/test/ui/lint/lint-ctypes-fn.rs | 8 +- src/test/ui/lint/lint-ctypes.rs | 14 +- ...ke-case-identifiers-suggestion-reserved.rs | 19 + ...ase-identifiers-suggestion-reserved.stderr | 67 + src/test/ui/lint/lint-removed-cmdline.stderr | 8 +- src/test/ui/lint/lint-removed.stderr | 2 +- .../ui/lint/lint-unexported-no-mangle.stderr | 16 +- .../lint}/must_use-in-stdlib-traits.rs | 0 .../ui/lint/must_use-in-stdlib-traits.stderr | 47 + .../redundant-semicolon/item-stmt-semi.rs | 8 +- .../redundant-semicolon/item-stmt-semi.stderr | 20 + ...ow-semicolon-in-expressions-from-macros.rs | 15 + .../semicolon-in-expressions-from-macros.rs | 41 + ...emicolon-in-expressions-from-macros.stderr | 33 + .../ui/lint/special-upper-lower-cases.stderr | 8 +- src/test/ui/{ => lint}/trivial_casts.rs | 0 src/test/ui/{ => lint}/trivial_casts.stderr | 0 src/test/ui/lint/unreachable_pub-pub_crate.rs | 2 +- src/test/ui/lint/unreachable_pub.rs | 2 +- .../warn-unused-inline-on-fn-prototypes.rs | 2 +- .../llvm-asm}/asm-src-loc-codegen-units.rs | 2 + .../llvm-asm}/asm-src-loc.rs | 2 + .../inline-asm-bad-constraint.rs | 0 .../inline-asm-bad-constraint.stderr | 0 .../{ => llvm-asm}/inline-asm-bad-operand.rs | 0 .../inline-asm-bad-operand.stderr | 0 .../ui/{issues => llvm-asm}/issue-14936.rs | 0 .../ui/{issues => llvm-asm}/issue-33264.rs | 0 .../loops/loop-break-value-no-repeat.stderr | 4 +- src/test/ui/loops/loop-break-value.rs | 1 - src/test/ui/loops/loop-break-value.stderr | 70 +- src/test/ui/macros/assert-macro-owned.rs | 2 + .../auxiliary/issue-40469.rs | 0 src/test/ui/macros/edition-macro-pats.rs | 14 + src/test/ui/{issues => macros}/issue-26322.rs | 0 src/test/ui/{issues => macros}/issue-40469.rs | 0 src/test/ui/{issues => macros}/issue-41803.rs | 0 src/test/ui/{issues => macros}/issue-5060.rs | 0 src/test/ui/macros/issue-81006.rs | 10 + src/test/ui/macros/issue-81006.stderr | 14 + src/test/ui/{issues => macros}/issue-8709.rs | 0 .../ui/macros/macro-comma-behavior-rpass.rs | 28 +- .../macros/macro-comma-behavior.core.stderr | 14 +- src/test/ui/macros/macro-comma-behavior.rs | 20 +- .../ui/macros/macro-comma-behavior.std.stderr | 30 +- .../ui/macros/macro-comma-support-rpass.rs | 2 +- src/test/ui/macros/macros-in-extern.rs | 14 +- .../{compile-fail => ui/macros}/not-utf8.bin | Bin .../{compile-fail => ui/macros}/not-utf8.rs | 0 src/test/ui/macros/not-utf8.stderr | 10 + src/test/ui/macros/vec-macro-in-pattern.rs | 10 + .../ui/macros/vec-macro-in-pattern.stderr | 10 + .../match/match-pattern-field-mismatch.stderr | 9 + .../meta-expected-error-wrong-rev.a.stderr | 16 + .../meta}/meta-expected-error-wrong-rev.rs | 0 src/test/ui/methods/method-call-err-msg.rs | 2 +- .../ui/methods/method-call-err-msg.stderr | 6 +- .../methods/method-call-lifetime-args-fail.rs | 10 +- .../method-call-lifetime-args-fail.stderr | 120 +- src/test/ui/methods/method-lookup-order.rs | 190 + src/test/ui/{issues => mir}/issue-66851.rs | 0 src/test/ui/mir/issue-80742.rs | 33 + src/test/ui/mir/issue-80742.stderr | 70 + .../array-clone-with-generic-size.rs | 2 - .../inline-instrument-coverage-fail.rs | 21 + .../inline-instrument-coverage-fail.stderr | 2 + src/test/ui/mir/mir_codegen_calls.rs | 2 +- src/test/ui/mir/mir_drop_order.rs | 2 +- .../ui/mir/ssa-analysis-regression-50041.rs | 34 + src/test/ui/mismatched_types/E0053.stderr | 9 +- .../closure-arg-type-mismatch.stderr | 48 +- .../mismatched_types/closure-mismatch.stderr | 12 +- src/test/ui/mismatched_types/issue-26480.rs | 2 +- src/test/ui/mismatched_types/issue-36053-2.rs | 2 +- .../ui/mismatched_types/issue-36053-2.stderr | 6 +- .../method-help-unsatisfied-bound.rs | 2 +- .../method-help-unsatisfied-bound.stderr | 6 +- .../overloaded-calls-bad.stderr | 12 + .../trait-impl-fn-incompatibility.stderr | 9 +- src/test/ui/moves/move-fn-self-receiver.rs | 5 + .../ui/moves/move-fn-self-receiver.stderr | 23 +- ...moves-based-on-type-access-to-field.stderr | 2 +- .../ui/moves/moves-based-on-type-exprs.stderr | 4 +- .../ui/{issues => never_type}/issue-10176.rs | 0 .../{issues => never_type}/issue-10176.stderr | 0 .../never_type}/issue-52443.rs | 0 src/test/ui/never_type/issue-52443.stderr | 60 + src/test/ui/nil-decl-in-foreign.rs | 4 +- .../escape-upvar-nested.stderr | 8 +- .../escape-upvar-ref.stderr | 4 +- ...er-to-static-comparing-against-free.stderr | 3 +- ...oximated-shorter-to-static-no-bound.stderr | 1 + ...mated-shorter-to-static-wrong-bound.stderr | 1 + .../issue-45696-long-live-borrows-in-boxes.rs | 0 .../issue-45696-scribble-on-boxed-borrow.rs | 0 ...ssue-45696-scribble-on-boxed-borrow.stderr | 0 src/test/ui/{issues => nll}/issue-46036.rs | 0 .../ui/{issues => nll}/issue-46036.stderr | 0 src/test/ui/{issues => nll}/issue-51345-2.rs | 0 src/test/ui/{issues => nll}/issue-51770.rs | 0 src/test/ui/{issues => nll}/issue-54943-3.rs | 0 src/test/ui/{issues => nll}/issue-55511.rs | 0 .../ui/{issues => nll}/issue-55511.stderr | 0 .../nll/issue-57642-higher-ranked-subtype.rs | 2 +- .../issue-57642-higher-ranked-subtype.stderr | 6 +- .../ui/nll/outlives-suggestion-simple.stderr | 1 + .../user-annotations/closure-substs.stderr | 1 + src/test/ui/no-patterns-in-args-2.stderr | 2 +- src/test/ui/no-patterns-in-args.rs | 4 +- src/test/ui/no_owned_box_lang_item.rs | 2 +- src/test/ui/non-built-in-quote.rs | 8 - .../ui/non-constant-expr-for-arr-len.stderr | 4 +- .../ui/{panic-brace.rs => non-fmt-panic.rs} | 12 +- ...anic-brace.stderr => non-fmt-panic.stderr} | 114 +- src/test/ui/nullable-pointer-size.rs | 2 +- .../issue-8460-const.noopt.stderr | 0 .../issue-8460-const.opt.stderr | 0 ...8460-const.opt_with_overflow_checks.stderr | 0 .../issue-8460-const.rs | 0 .../issue-8460.rs | 0 .../object-lifetime-default-from-rptr-box.rs | 2 +- .../object-lifetime-default-mybox.nll.stderr | 1 + .../macro-pat.rs} | 0 src/test/ui/osx-frameworks.rs | 5 +- src/test/ui/overloaded-calls-nontuple.rs | 4 +- src/test/ui/overloaded-calls-nontuple.stderr | 4 +- .../auxiliary/weak-lang-items.rs | 0 .../panic-handler}/panic-handler-missing.rs | 1 + .../panic-handler}/panic-handler-twice.rs | 1 + .../panic-handler}/weak-lang-item.rs | 0 .../ui/panic-handler/weak-lang-item.stderr | 19 + .../panic-runtime}/auxiliary/depends.rs | 0 .../auxiliary/needs-panic-runtime.rs | 0 .../auxiliary/panic-runtime-abort.rs | 6 +- .../auxiliary/panic-runtime-unwind.rs | 6 +- .../auxiliary/panic-runtime-unwind2.rs | 6 +- .../runtime-depend-on-needs-runtime.rs | 1 + .../panic-runtime}/two-panic-runtimes.rs | 2 + .../unwind-tables-panic-required.rs | 1 + .../unwind-tables-target-required.rs | 0 .../panic-runtime}/want-abort-got-unwind.rs | 2 + .../panic-runtime}/want-abort-got-unwind2.rs | 2 + src/test/ui/panics/explicit-panic-msg.rs | 1 + src/test/ui/panics/panic-2021.rs | 9 + src/test/ui/panics/panic-2021.stderr | 42 + src/test/ui/panics/panic-macro-any-wrapped.rs | 2 + src/test/ui/panics/panic-macro-any.rs | 1 + src/test/ui/panics/while-panic.rs | 2 +- .../ui/parser/ascii-only-character-escape.rs | 6 +- .../parser/ascii-only-character-escape.stderr | 12 +- src/test/ui/parser/attrs-after-extern-mod.rs | 2 +- .../ui/parser/attrs-after-extern-mod.stderr | 4 +- .../auxiliary/issue-21146-inc.rs | 0 src/test/ui/parser/bad-char-literals.rs | 8 +- src/test/ui/parser/bad-char-literals.stderr | 16 +- src/test/ui/parser/block-no-opening-brace.rs | 2 +- .../ui/parser/block-no-opening-brace.stderr | 12 +- src/test/ui/parser/byte-literals.rs | 4 +- src/test/ui/parser/byte-literals.stderr | 25 +- src/test/ui/parser/byte-string-literals.rs | 5 +- .../ui/parser/byte-string-literals.stderr | 27 +- .../ui/{ => parser}/can-begin-expr-check.rs | 0 .../{ => parser}/can-begin-expr-check.stderr | 0 .../ui/parser/doc-before-extern-rbrace.rs | 2 +- src/test/ui/parser/duplicate-visibility.rs | 2 +- .../ui/parser/duplicate-visibility.stderr | 4 +- src/test/ui/parser/extern-no-fn.rs | 2 +- src/test/ui/parser/extern-no-fn.stderr | 4 +- src/test/ui/parser/fn-body-eq-expr-semi.rs | 2 +- .../ui/parser/fn-body-eq-expr-semi.stderr | 8 +- .../parser/fn-body-optional-semantic-fail.rs | 2 +- .../fn-body-optional-semantic-fail.stderr | 4 +- .../parser/fn-body-optional-syntactic-pass.rs | 4 +- src/test/ui/parser/fn-header-semantic-fail.rs | 2 +- .../ui/parser/fn-header-semantic-fail.stderr | 20 +- .../ui/parser/fn-header-syntactic-pass.rs | 12 +- .../ui/parser/foreign-const-semantic-fail.rs | 2 +- .../parser/foreign-const-semantic-fail.stderr | 4 +- .../ui/parser/foreign-const-syntactic-fail.rs | 2 +- .../ui/parser/foreign-static-semantic-fail.rs | 2 +- .../foreign-static-semantic-fail.stderr | 8 +- .../parser/foreign-static-syntactic-pass.rs | 6 +- src/test/ui/parser/import-from-path.stderr | 2 + src/test/ui/parser/import-from-rename.stderr | 2 + src/test/ui/parser/import-glob-path.stderr | 2 + src/test/ui/parser/import-glob-rename.stderr | 2 + src/test/ui/parser/issue-14303-enum.stderr | 2 +- src/test/ui/parser/issue-14303-fn-def.stderr | 2 +- src/test/ui/parser/issue-14303-impl.stderr | 2 +- src/test/ui/parser/issue-14303-struct.stderr | 2 +- src/test/ui/parser/issue-14303-trait.stderr | 2 +- .../ui/{issues => parser}/issue-20616-1.rs | 0 .../{issues => parser}/issue-20616-1.stderr | 0 .../ui/{issues => parser}/issue-20616-2.rs | 0 .../{issues => parser}/issue-20616-2.stderr | 0 .../ui/{issues => parser}/issue-20616-8.rs | 0 .../{issues => parser}/issue-20616-8.stderr | 0 .../ui/{issues => parser}/issue-20616-9.rs | 0 .../{issues => parser}/issue-20616-9.stderr | 0 src/test/ui/{issues => parser}/issue-21146.rs | 0 .../ui/{issues => parser}/issue-21146.stderr | 0 .../ui/parser/issue-23620-invalid-escapes.rs | 14 +- .../parser/issue-23620-invalid-escapes.stderr | 34 +- .../ui/{issues => parser}/issue-34222-1.rs | 0 .../{issues => parser}/issue-34222-1.stderr | 0 src/test/ui/{issues => parser}/issue-43196.rs | 0 .../ui/{issues => parser}/issue-43196.stderr | 0 src/test/ui/parser/issue-43692.stderr | 4 +- src/test/ui/{issues => parser}/issue-44021.rs | 0 .../ui/{issues => parser}/issue-44021.stderr | 0 src/test/ui/{issues => parser}/issue-45296.rs | 0 .../ui/{issues => parser}/issue-45296.stderr | 0 .../ui/{issues => parser}/issue-46186.fixed | 0 src/test/ui/{issues => parser}/issue-46186.rs | 0 .../ui/{issues => parser}/issue-46186.stderr | 0 src/test/ui/{issues => parser}/issue-51602.rs | 0 .../ui/{issues => parser}/issue-51602.stderr | 0 src/test/ui/{issues => parser}/issue-52496.rs | 0 .../ui/{issues => parser}/issue-52496.stderr | 0 src/test/ui/{issues => parser}/issue-57198.rs | 0 .../ui/{issues => parser}/issue-57198.stderr | 0 .../ui/{issues => parser}/issue-57684.fixed | 0 src/test/ui/{issues => parser}/issue-57684.rs | 0 .../ui/{issues => parser}/issue-57684.stderr | 0 .../ui/{issues => parser}/issue-57819.fixed | 0 src/test/ui/{issues => parser}/issue-57819.rs | 0 .../ui/{issues => parser}/issue-57819.stderr | 0 .../ui/{issues => parser}/issue-58856-1.rs | 0 .../{issues => parser}/issue-58856-1.stderr | 0 .../ui/{issues => parser}/issue-58856-2.rs | 0 .../{issues => parser}/issue-58856-2.stderr | 0 src/test/ui/{issues => parser}/issue-60075.rs | 0 .../ui/{issues => parser}/issue-60075.stderr | 0 src/test/ui/{issues => parser}/issue-62554.rs | 0 .../ui/{issues => parser}/issue-62554.stderr | 0 src/test/ui/parser/issue-62913.stderr | 2 +- src/test/ui/{issues => parser}/issue-64732.rs | 0 .../ui/{issues => parser}/issue-64732.stderr | 6 +- src/test/ui/{issues => parser}/issue-66473.rs | Bin .../ui/{issues => parser}/issue-66473.stderr | Bin src/test/ui/{issues => parser}/issue-72253.rs | 0 .../ui/{issues => parser}/issue-72253.stderr | 0 src/test/ui/{issues => parser}/issue-72373.rs | 0 .../ui/{issues => parser}/issue-72373.stderr | 0 src/test/ui/{issues => parser}/issue-75599.rs | 0 src/test/ui/{ => parser}/issue-76597.fixed | 0 src/test/ui/{ => parser}/issue-76597.rs | 0 src/test/ui/{ => parser}/issue-76597.stderr | 0 src/test/ui/{issues => parser}/issue-7970b.rs | 0 .../ui/{issues => parser}/issue-7970b.stderr | 0 src/test/ui/parser/issue-81806.rs | 5 + src/test/ui/parser/issue-81806.stderr | 17 + src/test/ui/parser/issue-8537.stderr | 2 +- .../ui/parser/lex-bad-char-literals-1.stderr | 8 +- src/test/ui/parser/lex-bad-char-literals-7.rs | 2 +- .../ui/parser/lex-bad-char-literals-7.stderr | 6 +- .../lex-bare-cr-string-literal-doc-comment.rs | 2 +- ...-bare-cr-string-literal-doc-comment.stderr | 6 +- .../lifetime_starts_expressions.rs | 0 .../lifetime_starts_expressions.stderr | 0 ...rals-are-validated-before-expansion.stderr | 4 +- ...ing-closing-angle-bracket-eq-constraint.rs | 23 + ...closing-angle-bracket-eq-constraint.stderr | 49 + ...ultibyte-char-use-seperator-issue-80134.rs | 12 + ...byte-char-use-seperator-issue-80134.stderr | 52 + .../nested-missing-closing-angle-bracket.rs | 4 + ...ested-missing-closing-angle-bracket.stderr | 8 + src/test/ui/parser/new-unicode-escapes-1.rs | 2 +- .../ui/parser/new-unicode-escapes-1.stderr | 9 +- src/test/ui/parser/new-unicode-escapes-2.rs | 2 +- .../ui/parser/new-unicode-escapes-2.stderr | 4 +- .../ui/parser/new-unicode-escapes-3.stderr | 4 +- src/test/ui/parser/new-unicode-escapes-4.rs | 2 +- .../ui/parser/new-unicode-escapes-4.stderr | 4 +- .../ui/parser/no-const-fn-in-extern-block.rs | 2 +- .../parser/no-const-fn-in-extern-block.stderr | 8 +- .../ui/{ => parser}/parse-assoc-type-lt.rs | 0 .../ui/{ => parser}/parse-error-correct.rs | 0 .../{ => parser}/parse-error-correct.stderr | 0 src/test/ui/{ => parser}/parse-panic.rs | 0 src/test/ui/{ => parser}/parser-recovery-1.rs | 0 .../ui/{ => parser}/parser-recovery-1.stderr | 0 src/test/ui/{ => parser}/parser-recovery-2.rs | 0 .../ui/{ => parser}/parser-recovery-2.stderr | 0 .../{ => parser}/parser-unicode-whitespace.rs | 0 .../raw/raw-byte-string-literals.stderr | 2 +- .../ui/parser/self-param-semantic-fail.rs | 6 +- .../ui/parser/self-param-syntactic-pass.rs | 2 +- .../trailing-carriage-return-in-string.rs | 4 +- .../trailing-carriage-return-in-string.stderr | 2 +- src/test/ui/parser/unsafe-foreign-mod.rs | 4 - src/test/ui/parser/unsafe-foreign-mod.stderr | 8 +- .../variadic-ffi-semantic-restrictions.rs | 10 +- .../variadic-ffi-semantic-restrictions.stderr | 30 +- .../ui/parser/wrong-escape-of-curly-braces.rs | 8 +- .../wrong-escape-of-curly-braces.stderr | 4 +- .../ui/{issues => pattern}/issue-12582.rs | 0 .../ui/{issues => pattern}/issue-14221.rs | 0 .../ui/{issues => pattern}/issue-14221.stderr | 0 .../ui/{issues => pattern}/issue-22546.rs | 0 src/test/ui/{issues => pattern}/issue-6449.rs | 0 ...67037-pat-tup-scrut-ty-diff-less-fields.rs | 0 ...7-pat-tup-scrut-ty-diff-less-fields.stderr | 9 + src/test/ui/pattern/issue-74539.stderr | 5 + src/test/ui/pattern/pat-tuple-underfield.rs | 55 + .../ui/pattern/pat-tuple-underfield.stderr | 131 + src/test/ui/{ => pattern}/size-and-align.rs | 0 .../always-inhabited-union-ref.stderr | 1 + .../ui/pattern/usefulness/auxiliary/empty.rs | 8 + ...=> empty-match.exhaustive_patterns.stderr} | 160 +- ...empty.stderr => empty-match.normal.stderr} | 152 +- src/test/ui/pattern/usefulness/empty-match.rs | 95 + .../integer-ranges/pointer-sized-int-allow.rs | 38 - ....stderr => pointer-sized-int.allow.stderr} | 2 +- ...y.stderr => pointer-sized-int.deny.stderr} | 24 +- ...sized-int-deny.rs => pointer-sized-int.rs} | 24 +- .../issue-78123-non-exhaustive-reference.rs | 11 + ...ssue-78123-non-exhaustive-reference.stderr | 16 + .../match-empty-exhaustive_patterns.rs | 118 - src/test/ui/pattern/usefulness/match-empty.rs | 118 - src/test/ui/pattern/usefulness/uninhabited.rs | 143 + .../const_parameters/closures.stderr | 1 - .../const_parameters/functions.stderr | 1 - .../ui/polymorphization/generators.stderr | 1 - .../ui/{issues => privacy}/issue-29161.rs | 0 .../ui/{issues => privacy}/issue-29161.stderr | 0 .../ui/{issues => privacy}/issue-30079.rs | 0 .../ui/{issues => privacy}/issue-30079.stderr | 0 ...sue-46209-private-enum-variant-reexport.rs | 0 ...46209-private-enum-variant-reexport.stderr | 44 + .../ui/{ => privacy}/priv-in-bad-locations.rs | 2 +- .../priv-in-bad-locations.stderr | 2 +- src/test/ui/privacy/privacy1.rs | 2 +- src/test/ui/privacy/private-in-public-warn.rs | 2 +- src/test/ui/privacy/pub-extern-privacy.rs | 2 +- .../ui/proc-macro/ambiguous-builtin-attrs.rs | 10 +- .../proc-macro/ambiguous-builtin-attrs.stderr | 27 +- .../auxiliary/nonterminal-recollect-attr.rs | 23 + .../proc-macro/issue-75930-derive-cfg.stdout | 256 +- .../issue-78675-captured-inner-attrs.stdout | 18 +- .../ui/proc-macro/issue-80760-empty-stmt.rs | 26 + .../proc-macro/issue-80760-empty-stmt.stdout | 14 + .../ui/proc-macro/issue-81007-item-attrs.rs | 31 + .../proc-macro/issue-81007-item-attrs.stdout | 99 + .../proc-macro/issue-81543-item-parse-err.rs | 14 + .../issue-81543-item-parse-err.stderr | 8 + src/test/ui/proc-macro/lifetimes.stderr | 7 +- .../ui/proc-macro/macros-in-extern-derive.rs | 2 +- src/test/ui/proc-macro/macros-in-extern.rs | 6 +- .../proc-macro/nonterminal-recollect-attr.rs | 17 + src/test/ui/proc-macro/signature.rs | 2 +- src/test/ui/proc-macro/signature.stderr | 2 +- src/test/ui/proc-macro/span-preservation.rs | 2 +- .../ui/proc-macro/span-preservation.stderr | 4 +- src/test/ui/range/issue-54505-no-std.rs | 2 +- .../{ => reachable}/unreachable-code-ret.rs | 0 .../unreachable-code-ret.stderr | 0 .../ui/{issues => regions}/issue-12470.rs | 0 .../ui/{issues => regions}/issue-12470.stderr | 0 .../ui/{issues => regions}/issue-24085.rs | 0 src/test/ui/{issues => regions}/issue-2718.rs | 0 .../issue-28848.nll.stderr | 0 .../ui/{issues => regions}/issue-28848.rs | 0 .../ui/{issues => regions}/issue-28848.stderr | 0 src/test/ui/{issues => regions}/issue-5243.rs | 0 src/test/ui/regions/issue-78262.nll.stderr | 1 + ...nvariant-static-error-reporting.nll.stderr | 1 + ...hod-type-parameters-trait-bound.nll.stderr | 1 + .../regions-early-bound-trait-param.rs | 2 +- .../ui/regions/regions-nested-fns.nll.stderr | 3 +- ...ariance-contravariant-use-contravariant.rs | 2 +- ...egions-variance-covariant-use-covariant.rs | 2 +- src/test/ui/repeat_count.stderr | 2 + .../ui/resolve/crate-called-as-function.rs | 3 + .../resolve/crate-called-as-function.stderr | 9 + .../ui/{issues => resolve}/issue-10200.rs | 0 .../ui/{issues => resolve}/issue-10200.stderr | 0 .../ui/{issues => resolve}/issue-50599.rs | 0 .../ui/{issues => resolve}/issue-50599.stderr | 0 ...e-65035-static-with-parent-generics.stderr | 1 - src/test/ui/resolve/issue-82156.rs | 3 + src/test/ui/resolve/issue-82156.stderr | 9 + src/test/ui/resolve/missing-in-namespace.rs | 4 + .../ui/resolve/missing-in-namespace.stderr | 14 + .../resolve/resolve-primitive-fallback.stderr | 6 + .../improper_ctypes/extern_crate_improper.rs | 6 +- .../improper_ctypes/same_crate_proper.rs | 17 +- .../non-existent-2.rs | 2 +- .../non-existent-2.stderr | 4 +- .../disallowed-positions.stderr | 1 - .../feature-gate-raw-dylib-2.rs | 4 +- .../feature-gate-raw-dylib.rs | 4 +- .../feature-gate-raw-dylib.stderr | 4 +- .../link-ordinal-and-name.rs | 2 +- .../link-ordinal-invalid-format.rs | 4 +- .../link-ordinal-too-large.rs | 4 +- .../ui/{ => rmeta}/auxiliary/rmeta-meta.rs | 0 .../{ => rmeta}/auxiliary/rmeta-rlib-rpass.rs | 0 .../ui/{ => rmeta}/auxiliary/rmeta-rlib.rs | 0 .../ui/{ => rmeta}/auxiliary/rmeta-rmeta.rs | 0 src/test/ui/{ => rmeta}/rmeta-lib-pass.rs | 0 src/test/ui/{ => rmeta}/rmeta-pass.rs | 0 src/test/ui/{ => rmeta}/rmeta-priv-warn.rs | 0 src/test/ui/{ => rmeta}/rmeta-rpass.rs | 0 src/test/ui/{ => rmeta}/rmeta.rs | 0 src/test/ui/{ => rmeta}/rmeta.stderr | 0 src/test/ui/{ => rmeta}/rmeta_lib.rs | 0 src/test/ui/{ => rmeta}/rmeta_lib.stderr | 0 src/test/ui/{ => rmeta}/rmeta_meta_main.rs | 0 .../ui/{ => rmeta}/rmeta_meta_main.stderr | 0 src/test/ui/safe-extern-statics-mut.rs | 2 +- src/test/ui/safe-extern-statics.rs | 2 +- .../ui/sanitize/unsupported-target.stderr | 2 +- src/test/ui/sepcomp/sepcomp-extern.rs | 2 +- src/test/ui/seq-args.rs | 16 +- src/test/ui/seq-args.stderr | 30 +- src/test/ui/signal-alternate-stack-cleanup.rs | 4 +- src/test/ui/{issues => simd}/issue-32947.rs | 0 src/test/ui/simd/simd-array-type.rs | 2 +- src/test/ui/simd/simd-generics.rs | 2 +- ...intrinsic-generic-arithmetic-saturating.rs | 2 +- .../simd/simd-intrinsic-generic-arithmetic.rs | 2 +- src/test/ui/similar-tokens.fixed | 13 - src/test/ui/similar-tokens.rs | 2 - src/test/ui/similar-tokens.stderr | 2 +- src/test/ui/simple_global_asm.rs | 14 +- src/test/ui/span/dropck_arr_cycle_checked.rs | 2 +- src/test/ui/span/dropck_vec_cycle_checked.rs | 2 +- src/test/ui/span/import-ty-params.rs | 3 + src/test/ui/span/import-ty-params.stderr | 16 +- src/test/ui/span/lint-unused-unsafe.rs | 2 +- src/test/ui/span/visibility-ty-params.stderr | 8 +- .../specialization/defaultimpl/projection.rs | 2 +- .../specialization-trait-not-implemented.rs | 2 +- ...pecialization-trait-not-implemented.stderr | 6 +- .../specialization/issue-50452-fail.rs} | 1 - .../ui/specialization/issue-50452-fail.stderr | 26 + .../repeated_projection_type.stderr | 2 +- .../specialization-projection.rs | 2 +- .../stability-attribute-trait-impl.rs | 2 +- .../stability-attribute-trait-impl.stderr | 2 +- .../static-mut-foreign-requires-unsafe.rs | 6 +- src/test/ui/static_sized_requirement.rs | 2 +- src/test/ui/stmt_expr_attrs_no_feature.rs | 2 +- src/test/ui/structs-enums/class-dtor.rs | 4 +- .../structs-enums/discrim-explicit-23030.rs | 2 +- src/test/ui/structs-enums/foreign-struct.rs | 6 +- ...bject-lifetime-default-from-rptr-struct.rs | 2 +- .../structs/struct-pat-derived-error.stderr | 4 +- .../structure-constructor-type-mismatch.rs | 6 +- ...structure-constructor-type-mismatch.stderr | 50 +- .../suggestions/borrow-for-loop-head.stderr | 8 +- src/test/ui/suggestions/field-access.fixed | 35 + src/test/ui/suggestions/field-access.rs | 35 + src/test/ui/suggestions/field-access.stderr | 67 + ...rait-with-implicit-static-bound.nll.stderr | 1 + src/test/ui/suggestions/issue-81098.rs | 13 + src/test/ui/suggestions/issue-81098.stderr | 23 + .../suggestions/missing-lifetime-specifier.rs | 16 +- .../missing-lifetime-specifier.stderr | 128 +- .../missing-trait-bounds-for-method-call.rs | 4 +- ...issing-trait-bounds-for-method-call.stderr | 12 +- .../suggestions/mut-borrow-needed-by-trait.rs | 2 +- .../mut-borrow-needed-by-trait.stderr | 6 +- ...eld-present-in-subfield-recursion-limit.rs | 43 + ...present-in-subfield-recursion-limit.stderr | 11 + ...n-existent-field-present-in-subfield.fixed | 42 + .../non-existent-field-present-in-subfield.rs | 42 + ...-existent-field-present-in-subfield.stderr | 27 + .../suggestions/suggest-move-lifetimes.stderr | 8 +- ...use-type-argument-instead-of-assoc-type.rs | 2 +- ...type-argument-instead-of-assoc-type.stderr | 16 +- .../ui/suggestions/vec-macro-in-pattern.fixed | 8 - .../ui/suggestions/vec-macro-in-pattern.rs | 8 - .../suggestions/vec-macro-in-pattern.stderr | 16 - src/test/ui/svh/auxiliary/svh-uta-base.rs | 2 +- .../svh/auxiliary/svh-uta-change-use-trait.rs | 2 +- src/test/ui/svh/auxiliary/svh-utb.rs | 2 +- src/test/ui/svh/svh-use-trait.rs | 2 +- .../symbol-names/const-generics-demangling.rs | 3 +- .../const-generics-demangling.stderr | 24 +- src/test/ui/symbol-names/const-generics.rs | 122 +- src/test/ui/symbol-names/impl1.rs | 2 +- src/test/ui/symbol-names/issue-76365.rs | 3 +- src/test/ui/tag-type-args.rs | 2 +- src/test/ui/tag-type-args.stderr | 12 +- src/test/ui/terminal-width/tabs-trimming.rs | 13 + .../ui/terminal-width/tabs-trimming.stderr | 12 + .../issue-53675-a-test-called-panic.rs | 0 .../eprint-on-tls-drop.rs | 0 .../threads-sendsync}/issue-43733-2.rs | 2 + .../issue-43733.rs | 0 .../issue-43733.stderr | 0 .../issue-4446.rs | 0 .../thread-local-extern-static.rs | 2 +- .../issue-33140-hack-boundaries.rs | 0 .../issue-33140-hack-boundaries.stderr | 0 src/test/ui/{issues => traits}/issue-3683.rs | 0 src/test/ui/{issues => traits}/issue-56202.rs | 0 src/test/ui/{issues => traits}/issue-56488.rs | 0 .../ui/{issues => traits}/issue-59029-2.rs | 0 src/test/ui/{issues => traits}/issue-6128.rs | 0 src/test/ui/{issues => traits}/issue-6334.rs | 0 ...issue-65284-suggest-generic-trait-bound.rs | 0 ...e-65284-suggest-generic-trait-bound.stderr | 0 src/test/ui/{issues => traits}/issue-65673.rs | 0 .../ui/{issues => traits}/issue-65673.stderr | 0 .../issue-9394-inherited-trait-calls.rs | 0 .../ui/traits/mutual-recursion-issue-75860.rs | 15 + .../mutual-recursion-issue-75860.stderr | 16 + .../ui/traits/trait-object-vs-lifetime.rs | 4 +- .../ui/traits/trait-object-vs-lifetime.stderr | 28 +- src/test/ui/traits/trait-test-2.rs | 6 +- src/test/ui/traits/trait-test-2.stderr | 32 +- .../ui/traits/traits-repeated-supertrait.rs | 2 +- .../try-block/try-block-in-edition2015.stderr | 2 +- .../enum-variant-generic-args.rs | 26 +- .../enum-variant-generic-args.stderr | 84 +- ...priority-higher-than-other-inherent.stderr | 6 - .../assoc-type-const.stderr | 1 - .../issue-57611-trait-alias.nll.stderr | 10 + .../issue-57611-trait-alias.stderr | 25 + src/test/ui/type-param.rs | 2 +- src/test/ui/type-params-in-for-each.rs | 2 +- src/test/ui/type/ascription/issue-34255-1.rs | 2 +- .../ui/type/ascription/issue-34255-1.stderr | 14 +- .../ui/type/ascription/issue-47666.stderr | 2 +- ...e-ascription-instead-of-initializer.stderr | 6 + .../type-dependent-def-issue-49241.stderr | 4 +- src/test/ui/typeck/issue-81293.rs | 9 + src/test/ui/typeck/issue-81293.stderr | 24 + .../typeck-builtin-bound-type-parameters.rs | 16 +- ...ypeck-builtin-bound-type-parameters.stderr | 84 +- .../ui/typeck/typeck_type_placeholder_item.rs | 12 + .../typeck_type_placeholder_item.stderr | 22 +- .../typeck_type_placeholder_item_help.rs | 13 +- .../typeck_type_placeholder_item_help.stderr | 19 +- .../typeck_type_placeholder_lifetime_1.rs | 2 +- .../typeck_type_placeholder_lifetime_1.stderr | 14 +- .../typeck_type_placeholder_lifetime_2.rs | 2 +- .../typeck_type_placeholder_lifetime_2.stderr | 14 +- src/test/ui/typestate-cfg-nesting.rs | 20 - src/test/ui/ufcs/ufcs-qpath-missing-params.rs | 6 +- .../ui/ufcs/ufcs-qpath-missing-params.stderr | 48 +- .../ui/unboxed-closures/issue-30906.stderr | 7 +- .../unboxed-closure-sugar-region.rs | 2 +- .../unboxed-closure-sugar-region.stderr | 10 +- .../unboxed-closure-sugar-used-on-struct-1.rs | 2 +- ...oxed-closure-sugar-used-on-struct-1.stderr | 14 +- .../unboxed-closure-sugar-used-on-struct.rs | 2 +- ...nboxed-closure-sugar-used-on-struct.stderr | 14 +- ...r-wrong-number-number-type-parameters-3.rs | 2 +- ...ong-number-number-type-parameters-3.stderr | 12 +- ...gar-wrong-number-number-type-parameters.rs | 24 +- ...wrong-number-number-type-parameters.stderr | 92 +- .../unboxed-closure-sugar-wrong-trait.rs | 4 +- .../unboxed-closure-sugar-wrong-trait.stderr | 12 +- ...es-infer-fnmut-calling-fnmut-no-mut.stderr | 4 + ...ed-closures-infer-fnmut-missing-mut.stderr | 4 +- ...osures-infer-fnmut-move-missing-mut.stderr | 4 +- src/test/ui/underscore-imports/hygiene.rs | 10 +- src/test/ui/underscore-imports/hygiene.stderr | 27 - .../uninhabited-matches-feature-gated.stderr | 1 + src/test/ui/union/union-derive-clone.rs | 2 +- src/test/ui/union/union-derive-clone.stderr | 6 +- src/test/ui/unique-object-noncopyable.rs | 2 +- src/test/ui/unique-object-noncopyable.stderr | 6 +- src/test/ui/unique-pinned-nocopy.rs | 2 +- src/test/ui/unique-pinned-nocopy.stderr | 6 +- src/test/ui/unique/unique-ffi-symbols.rs | 8 +- .../ui/unsafe-fn-called-from-unsafe-blk.rs | 2 +- .../ui/unsafe-fn-called-from-unsafe-fn.rs | 2 +- .../issue-45087-unreachable-unsafe.rs | 0 .../issue-45087-unreachable-unsafe.stderr | 0 src/test/ui/unsafe/ranged_ints2_const.rs | 6 + src/test/ui/unsafe/ranged_ints2_const.stderr | 11 +- src/test/ui/unsafe/ranged_ints3_const.rs | 4 +- src/test/ui/unsafe/ranged_ints3_const.stderr | 12 +- .../unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs | 5 + .../rfc-2585-unsafe_op_in_unsafe_fn.stderr | 50 +- src/test/ui/unsafe/unsafe-move-val-init.rs | 10 - .../ui/unsafe/unsafe-move-val-init.stderr | 11 - .../unsized-locals/borrow-after-move.stderr | 2 +- src/test/ui/unsized-locals/double-move.stderr | 2 +- src/test/ui/unsized/unchanged-param.rs | 12 + src/test/ui/unsupported-cast.rs | 7 - src/test/ui/unused/unused-closure.rs | 5 - src/test/ui/unused/unused-closure.stderr | 28 +- .../use-after-move-self-based-on-type.stderr | 2 +- src/test/ui/use/use-after-move-self.stderr | 2 +- src/test/ui/walk-struct-literal-with.stderr | 2 +- src/test/ui/warn-ctypes-inhibit.rs | 6 +- src/test/ui/wasm-import-module.rs | 6 +- src/test/ui/wasm/wasm-hang-issue-76281.rs | 12 + .../wf/wf-in-foreign-fn-decls-issue-80468.rs | 17 + .../wf-in-foreign-fn-decls-issue-80468.stderr | 24 + src/tools/compiletest/src/common.rs | 13 +- src/tools/compiletest/src/header.rs | 5 +- src/tools/compiletest/src/header/tests.rs | 1 + src/tools/compiletest/src/main.rs | 4 +- src/tools/compiletest/src/runtest.rs | 43 +- src/tools/compiletest/src/util.rs | 16 +- src/tools/compiletest/src/util/tests.rs | 19 + src/tools/jsondocck/Cargo.toml | 14 + src/tools/jsondocck/src/cache.rs | 69 + src/tools/jsondocck/src/config.rs | 37 + src/tools/jsondocck/src/error.rs | 28 + src/tools/jsondocck/src/main.rs | 249 + src/tools/linkchecker/main.rs | 11 +- src/tools/lint-docs/src/groups.rs | 22 +- .../rust-installer/.github/workflows/ci.yml | 23 + src/tools/rust-installer/.travis.yml | 9 - src/tools/rust-installer/src/combiner.rs | 38 +- src/tools/rust-installer/src/compression.rs | 154 + src/tools/rust-installer/src/generator.rs | 9 +- src/tools/rust-installer/src/lib.rs | 1 + src/tools/rust-installer/src/main.rs | 10 +- src/tools/rust-installer/src/main.yml | 15 + src/tools/rust-installer/src/tarballer.rs | 76 +- src/tools/rust-installer/src/util.rs | 4 +- src/tools/rust-installer/test.sh | 173 + src/tools/rustbook/Cargo.toml | 2 +- src/tools/rustdoc-js/tester.js | 7 +- src/tools/tidy/src/cargo.rs | 90 - src/tools/tidy/src/deps.rs | 1 - src/tools/tidy/src/edition.rs | 16 +- src/tools/tidy/src/features.rs | 4 +- src/tools/tidy/src/lib.rs | 1 - src/tools/tidy/src/main.rs | 4 - src/tools/tidy/src/ui_tests.rs | 6 +- src/version | 2 +- vendor/addr2line/.cargo-checksum.json | 2 +- vendor/addr2line/CHANGELOG.md | 14 + vendor/addr2line/Cargo.lock | 6 +- vendor/addr2line/Cargo.toml | 6 +- vendor/addr2line/examples/addr2line.rs | 2 + vendor/addr2line/src/lib.rs | 324 +- vendor/anyhow/.cargo-checksum.json | 2 +- vendor/anyhow/Cargo.toml | 6 +- vendor/anyhow/build.rs | 30 +- vendor/anyhow/src/context.rs | 2 +- vendor/anyhow/src/error.rs | 275 +- vendor/anyhow/src/fmt.rs | 15 +- vendor/anyhow/src/lib.rs | 41 +- vendor/anyhow/src/macros.rs | 11 +- vendor/anyhow/src/ptr.rs | 126 + vendor/anyhow/tests/drop/mod.rs | 2 + vendor/anyhow/tests/test_convert.rs | 2 + vendor/anyhow/tests/test_downcast.rs | 8 + vendor/anyhow/tests/test_ffi.rs | 18 + vendor/anyhow/tests/test_macros.rs | 11 +- vendor/anyhow/tests/ui/temporary-value.rs | 5 + vendor/anyhow/tests/ui/temporary-value.stderr | 8 + vendor/arrayvec/.cargo-checksum.json | 2 +- vendor/arrayvec/CHANGELOG.md | 234 + vendor/arrayvec/Cargo.toml | 3 +- vendor/arrayvec/README.md | 24 + vendor/arrayvec/README.rst | 259 - vendor/arrayvec/ci/miri.sh | 15 + vendor/arrayvec/src/array.rs | 7 + vendor/arrayvec/src/array_string.rs | 23 +- vendor/arrayvec/src/char.rs | 1 + vendor/arrayvec/src/errors.rs | 6 +- vendor/arrayvec/src/lib.rs | 79 +- vendor/arrayvec/src/maybe_uninit.rs | 2 +- vendor/arrayvec/tests/tests.rs | 9 + vendor/byteorder/.cargo-checksum.json | 2 +- vendor/byteorder/CHANGELOG.md | 25 + vendor/byteorder/Cargo.toml | 15 +- vendor/byteorder/README.md | 6 +- vendor/byteorder/benches/bench.rs | 236 +- vendor/byteorder/build.rs | 90 - vendor/byteorder/rustfmt.toml | 2 + vendor/byteorder/src/io.rs | 92 +- vendor/byteorder/src/lib.rs | 1725 +- .../.cargo-checksum.json | 2 +- .../Cargo.toml | 4 +- .../LICENSE-MIT | 0 .../README.md | 0 .../src/dependency.rs | 0 .../src/diagnostic.rs | 0 .../src/errors.rs | 0 .../src/lib.rs | 5 - .../src/messages.rs | 0 .../tests/selftest.rs | 0 .../tests/test_samples.rs | 9 +- vendor/cc/.cargo-checksum.json | 2 +- vendor/cc/Cargo.lock | 2 +- vendor/cc/Cargo.toml | 2 +- vendor/cc/README.md | 46 +- vendor/cc/src/lib.rs | 158 +- vendor/chalk-derive/.cargo-checksum.json | 2 +- vendor/chalk-derive/Cargo.toml | 2 +- vendor/chalk-derive/src/lib.rs | 72 +- vendor/chalk-engine/.cargo-checksum.json | 2 +- vendor/chalk-engine/Cargo.toml | 8 +- vendor/chalk-engine/src/lib.rs | 4 +- vendor/chalk-engine/src/logic.rs | 530 +- vendor/chalk-engine/src/normalize_deep.rs | 63 +- vendor/chalk-engine/src/simplify.rs | 83 +- vendor/chalk-engine/src/slg.rs | 244 +- vendor/chalk-engine/src/slg/aggregate.rs | 2 +- vendor/chalk-engine/src/slg/resolvent.rs | 298 +- vendor/chalk-engine/src/stack.rs | 8 +- vendor/chalk-engine/src/strand.rs | 43 +- vendor/chalk-ir/.cargo-checksum.json | 2 +- vendor/chalk-ir/Cargo.toml | 7 +- vendor/chalk-ir/src/cast.rs | 6 + vendor/chalk-ir/src/could_match.rs | 126 +- vendor/chalk-ir/src/debug.rs | 32 +- vendor/chalk-ir/src/fold.rs | 318 +- vendor/chalk-ir/src/fold/binder_impls.rs | 41 +- vendor/chalk-ir/src/fold/boring_impls.rs | 208 +- vendor/chalk-ir/src/fold/in_place.rs | 263 + vendor/chalk-ir/src/fold/shift.rs | 36 +- vendor/chalk-ir/src/fold/subst.rs | 26 +- vendor/chalk-ir/src/interner.rs | 95 +- vendor/chalk-ir/src/lib.rs | 560 +- vendor/chalk-ir/src/visit.rs | 301 +- vendor/chalk-ir/src/visit/binder_impls.rs | 22 +- vendor/chalk-ir/src/visit/boring_impls.rs | 137 +- vendor/chalk-ir/src/visit/visitors.rs | 44 +- vendor/chalk-ir/src/zip.rs | 412 +- vendor/chalk-solve/.cargo-checksum.json | 2 +- vendor/chalk-solve/Cargo.toml | 6 +- vendor/chalk-solve/src/clauses.rs | 388 +- vendor/chalk-solve/src/clauses/builder.rs | 7 +- .../chalk-solve/src/clauses/builtin_traits.rs | 39 +- .../src/clauses/builtin_traits/clone.rs | 10 +- .../src/clauses/builtin_traits/copy.rs | 45 +- .../builtin_traits/discriminant_kind.rs | 73 + .../src/clauses/builtin_traits/fn_family.rs | 20 +- .../src/clauses/builtin_traits/sized.rs | 50 +- .../src/clauses/builtin_traits/unsize.rs | 66 +- vendor/chalk-solve/src/clauses/dyn_ty.rs | 13 +- .../chalk-solve/src/clauses/env_elaborator.rs | 59 +- vendor/chalk-solve/src/clauses/generalize.rs | 8 +- .../src/clauses/program_clauses.rs | 253 +- vendor/chalk-solve/src/coherence/solve.rs | 6 +- vendor/chalk-solve/src/display/items.rs | 18 +- vendor/chalk-solve/src/display/state.rs | 2 +- vendor/chalk-solve/src/display/stub.rs | 23 +- vendor/chalk-solve/src/display/ty.rs | 6 +- vendor/chalk-solve/src/ext.rs | 18 +- vendor/chalk-solve/src/goal_builder.rs | 9 +- vendor/chalk-solve/src/infer.rs | 42 +- vendor/chalk-solve/src/infer/canonicalize.rs | 36 +- vendor/chalk-solve/src/infer/instantiate.rs | 101 +- vendor/chalk-solve/src/infer/invert.rs | 14 +- vendor/chalk-solve/src/infer/test.rs | 174 +- vendor/chalk-solve/src/infer/ucanonicalize.rs | 37 +- vendor/chalk-solve/src/infer/unify.rs | 985 +- vendor/chalk-solve/src/infer/var.rs | 6 + vendor/chalk-solve/src/lib.rs | 7 +- vendor/chalk-solve/src/logging_db.rs | 57 +- .../src/logging_db/id_collector.rs | 13 +- vendor/chalk-solve/src/recursive/lib.rs | 196 - vendor/chalk-solve/src/rust_ir.rs | 46 +- vendor/chalk-solve/src/solve.rs | 106 +- vendor/chalk-solve/src/solve/truncate.rs | 23 +- vendor/chalk-solve/src/split.rs | 4 +- vendor/chalk-solve/src/wf.rs | 91 +- vendor/chrono/.cargo-checksum.json | 2 +- vendor/chrono/CHANGELOG.md | 33 +- vendor/chrono/Cargo.toml | 15 +- vendor/chrono/README.md | 20 +- vendor/chrono/src/datetime.rs | 2 +- vendor/chrono/src/format/parse.rs | 12 +- vendor/chrono/src/format/scan.rs | 6 + vendor/chrono/src/format/strftime.rs | 11 +- vendor/chrono/src/lib.rs | 35 +- vendor/chrono/src/naive/date.rs | 30 +- vendor/chrono/src/naive/datetime.rs | 72 +- vendor/chrono/src/naive/time.rs | 60 +- vendor/chrono/src/offset/local.rs | 21 +- vendor/chrono/src/offset/utc.rs | 13 +- vendor/chrono/src/sys.rs | 126 + vendor/chrono/src/sys/stub.rs | 80 + vendor/chrono/src/sys/unix.rs | 126 + vendor/chrono/src/sys/windows.rs | 131 + vendor/chrono/tests/wasm.rs | 15 +- vendor/cloudabi/.cargo-checksum.json | 1 - vendor/cloudabi/cloudabi.rs | 3030 --- vendor/cmake/.cargo-checksum.json | 2 +- vendor/cmake/Cargo.toml | 2 +- vendor/cmake/src/lib.rs | 135 +- vendor/compiler_builtins/.cargo-checksum.json | 2 +- vendor/compiler_builtins/Cargo.lock | 2 +- vendor/compiler_builtins/Cargo.toml | 4 +- vendor/compiler_builtins/src/arm.rs | 28 +- vendor/compiler_builtins/src/float/cmp.rs | 19 +- vendor/compiler_builtins/src/float/conv.rs | 6 +- vendor/compiler_builtins/src/float/div.rs | 10 +- vendor/compiler_builtins/src/float/mod.rs | 20 +- vendor/compiler_builtins/src/float/mul.rs | 30 +- vendor/compiler_builtins/src/int/addsub.rs | 84 +- .../src/int/leading_zeros.rs | 2 + vendor/compiler_builtins/src/int/mod.rs | 278 +- vendor/compiler_builtins/src/int/mul.rs | 171 +- vendor/compiler_builtins/src/int/sdiv.rs | 207 +- vendor/compiler_builtins/src/int/shift.rs | 72 +- .../src/int/specialized_div_rem/asymmetric.rs | 149 +- .../int/specialized_div_rem/binary_long.rs | 65 +- .../src/int/specialized_div_rem/delegate.rs | 199 +- .../src/int/specialized_div_rem/mod.rs | 116 +- .../src/int/specialized_div_rem/norm_shift.rs | 1 + .../src/int/specialized_div_rem/trifecta.rs | 106 +- vendor/compiler_builtins/src/int/udiv.rs | 38 +- vendor/compiler_builtins/src/lib.rs | 6 +- vendor/compiler_builtins/src/macros.rs | 8 +- vendor/compiler_builtins/src/math.rs | 4 + vendor/compiler_builtins/src/mem/impls.rs | 27 + .../src/{mem.rs => mem/mod.rs} | 42 +- vendor/compiler_builtins/src/mem/x86_64.rs | 100 + vendor/compiler_builtins/src/probestack.rs | 2 + vendor/compiler_builtins/src/x86.rs | 21 +- vendor/compiler_builtins/src/x86_64.rs | 21 +- vendor/const_fn/.cargo-checksum.json | 1 + vendor/const_fn/CHANGELOG.md | 149 + vendor/const_fn/Cargo.toml | 30 + vendor/const_fn/LICENSE-APACHE | 177 + vendor/const_fn/LICENSE-MIT | 23 + vendor/const_fn/README.md | 71 + vendor/const_fn/build.rs | 102 + vendor/const_fn/src/ast.rs | 141 + vendor/const_fn/src/error.rs | 37 + vendor/const_fn/src/iter.rs | 39 + vendor/const_fn/src/lib.rs | 233 + vendor/const_fn/src/to_tokens.rs | 36 + vendor/const_fn/src/utils.rs | 46 + vendor/crossbeam-channel/.cargo-checksum.json | 2 +- vendor/crossbeam-channel/CHANGELOG.md | 9 + vendor/crossbeam-channel/Cargo.lock | 193 +- vendor/crossbeam-channel/Cargo.toml | 23 +- vendor/crossbeam-channel/README.md | 21 +- vendor/crossbeam-channel/benches/crossbeam.rs | 3 - .../crossbeam-channel/examples/fibonacci.rs | 2 - vendor/crossbeam-channel/examples/matching.rs | 6 +- .../crossbeam-channel/examples/stopwatch.rs | 70 +- vendor/crossbeam-channel/src/channel.rs | 247 +- vendor/crossbeam-channel/src/context.rs | 2 +- vendor/crossbeam-channel/src/err.rs | 135 +- vendor/crossbeam-channel/src/flavors/array.rs | 56 +- .../src/flavors/{after.rs => at.rs} | 54 +- vendor/crossbeam-channel/src/flavors/list.rs | 44 +- vendor/crossbeam-channel/src/flavors/mod.rs | 4 +- vendor/crossbeam-channel/src/flavors/never.rs | 8 +- vendor/crossbeam-channel/src/flavors/tick.rs | 6 +- vendor/crossbeam-channel/src/flavors/zero.rs | 22 +- vendor/crossbeam-channel/src/lib.rs | 101 +- vendor/crossbeam-channel/src/select.rs | 185 +- vendor/crossbeam-channel/src/select_macro.rs | 446 +- vendor/crossbeam-channel/src/utils.rs | 10 +- vendor/crossbeam-channel/src/waker.rs | 20 +- vendor/crossbeam-channel/tests/after.rs | 7 +- vendor/crossbeam-channel/tests/array.rs | 7 +- vendor/crossbeam-channel/tests/golang.rs | 125 +- vendor/crossbeam-channel/tests/iter.rs | 3 - vendor/crossbeam-channel/tests/list.rs | 7 +- vendor/crossbeam-channel/tests/mpsc.rs | 43 +- vendor/crossbeam-channel/tests/never.rs | 6 +- vendor/crossbeam-channel/tests/ready.rs | 7 +- .../crossbeam-channel/tests/same_channel.rs | 2 - vendor/crossbeam-channel/tests/select.rs | 3 - .../crossbeam-channel/tests/select_macro.rs | 8 +- .../crossbeam-channel/tests/thread_locals.rs | 6 +- vendor/crossbeam-channel/tests/tick.rs | 7 +- vendor/crossbeam-channel/tests/zero.rs | 7 +- .../.cargo-checksum.json | 1 + vendor/crossbeam-deque-0.7.3/CHANGELOG.md | 95 + vendor/crossbeam-deque-0.7.3/Cargo.toml | 34 + .../LICENSE-APACHE | 0 vendor/crossbeam-deque-0.7.3/LICENSE-MIT | 27 + vendor/crossbeam-deque-0.7.3/README.md | 50 + vendor/crossbeam-deque-0.7.3/src/lib.rs | 2016 ++ vendor/crossbeam-deque-0.7.3/tests/fifo.rs | 342 + .../crossbeam-deque-0.7.3/tests/injector.rs | 353 + vendor/crossbeam-deque-0.7.3/tests/lifo.rs | 342 + vendor/crossbeam-deque-0.7.3/tests/steal.rs | 214 + vendor/crossbeam-deque/.cargo-checksum.json | 2 +- vendor/crossbeam-deque/CHANGELOG.md | 6 + vendor/crossbeam-deque/Cargo.toml | 25 +- vendor/crossbeam-deque/README.md | 20 +- vendor/crossbeam-deque/src/deque.rs | 1995 ++ vendor/crossbeam-deque/src/lib.rs | 1967 +- vendor/crossbeam-deque/tests/fifo.rs | 10 +- vendor/crossbeam-deque/tests/injector.rs | 10 +- vendor/crossbeam-deque/tests/lifo.rs | 10 +- vendor/crossbeam-deque/tests/steal.rs | 6 +- .../.cargo-checksum.json | 1 + vendor/crossbeam-epoch-0.8.2/CHANGELOG.md | 100 + vendor/crossbeam-epoch-0.8.2/Cargo.lock | 261 + vendor/crossbeam-epoch-0.8.2/Cargo.toml | 55 + .../LICENSE-APACHE | 0 vendor/crossbeam-epoch-0.8.2/LICENSE-MIT | 27 + vendor/crossbeam-epoch-0.8.2/README.md | 57 + vendor/crossbeam-epoch-0.8.2/benches/defer.rs | 71 + vendor/crossbeam-epoch-0.8.2/benches/flush.rs | 53 + vendor/crossbeam-epoch-0.8.2/benches/pin.rs | 32 + .../build.rs | 0 .../examples/sanitize.rs | 69 + .../examples/treiber_stack.rs | 110 + vendor/crossbeam-epoch-0.8.2/src/atomic.rs | 1201 + vendor/crossbeam-epoch-0.8.2/src/collector.rs | 434 + vendor/crossbeam-epoch-0.8.2/src/default.rs | 76 + vendor/crossbeam-epoch-0.8.2/src/deferred.rs | 136 + vendor/crossbeam-epoch-0.8.2/src/epoch.rs | 114 + vendor/crossbeam-epoch-0.8.2/src/guard.rs | 529 + vendor/crossbeam-epoch-0.8.2/src/internal.rs | 659 + vendor/crossbeam-epoch-0.8.2/src/lib.rs | 108 + vendor/crossbeam-epoch-0.8.2/src/sync/list.rs | 487 + vendor/crossbeam-epoch-0.8.2/src/sync/mod.rs | 4 + .../crossbeam-epoch-0.8.2/src/sync/queue.rs | 454 + vendor/crossbeam-epoch/.cargo-checksum.json | 2 +- vendor/crossbeam-epoch/CHANGELOG.md | 9 + vendor/crossbeam-epoch/Cargo.lock | 249 +- vendor/crossbeam-epoch/Cargo.toml | 29 +- vendor/crossbeam-epoch/README.md | 22 +- vendor/crossbeam-epoch/benches/defer.rs | 6 +- vendor/crossbeam-epoch/benches/flush.rs | 5 +- vendor/crossbeam-epoch/benches/pin.rs | 5 +- vendor/crossbeam-epoch/examples/sanitize.rs | 5 +- .../crossbeam-epoch/examples/treiber_stack.rs | 9 +- vendor/crossbeam-epoch/src/atomic.rs | 553 +- vendor/crossbeam-epoch/src/collector.rs | 26 +- vendor/crossbeam-epoch/src/default.rs | 5 +- vendor/crossbeam-epoch/src/deferred.rs | 9 +- vendor/crossbeam-epoch/src/guard.rs | 67 +- vendor/crossbeam-epoch/src/internal.rs | 42 +- vendor/crossbeam-epoch/src/lib.rs | 47 +- vendor/crossbeam-epoch/src/sync/list.rs | 16 +- vendor/crossbeam-epoch/src/sync/queue.rs | 24 +- vendor/crossbeam-utils/.cargo-checksum.json | 1 + vendor/crossbeam-utils/CHANGELOG.md | 129 + vendor/crossbeam-utils/Cargo.toml | 40 + vendor/crossbeam-utils/LICENSE-APACHE | 201 + vendor/crossbeam-utils/LICENSE-MIT | 27 + vendor/crossbeam-utils/README.md | 73 + vendor/crossbeam-utils/benches/atomic_cell.rs | 156 + vendor/crossbeam-utils/build.rs | 23 + .../crossbeam-utils/src/atomic/atomic_cell.rs | 971 + vendor/crossbeam-utils/src/atomic/consume.rs | 82 + vendor/crossbeam-utils/src/atomic/mod.rs | 27 + vendor/crossbeam-utils/src/atomic/seq_lock.rs | 112 + .../src/atomic/seq_lock_wide.rs | 155 + vendor/crossbeam-utils/src/backoff.rs | 285 + vendor/crossbeam-utils/src/cache_padded.rs | 139 + vendor/crossbeam-utils/src/lib.rs | 56 + vendor/crossbeam-utils/src/sync/mod.rs | 13 + vendor/crossbeam-utils/src/sync/parker.rs | 397 + .../crossbeam-utils/src/sync/sharded_lock.rs | 630 + vendor/crossbeam-utils/src/sync/wait_group.rs | 146 + vendor/crossbeam-utils/src/thread.rs | 588 + vendor/crossbeam-utils/tests/atomic_cell.rs | 236 + vendor/crossbeam-utils/tests/cache_padded.rs | 110 + vendor/crossbeam-utils/tests/parker.rs | 41 + vendor/crossbeam-utils/tests/sharded_lock.rs | 252 + vendor/crossbeam-utils/tests/thread.rs | 215 + vendor/crossbeam-utils/tests/wait_group.rs | 64 + vendor/dissimilar/.cargo-checksum.json | 1 + vendor/dissimilar/Cargo.toml | 26 + vendor/dissimilar/LICENSE-APACHE | 201 + vendor/dissimilar/LICENSE-MIT | 23 + vendor/dissimilar/README.md | 58 + vendor/dissimilar/benches/bench.rs | 15 + vendor/dissimilar/benches/document1.txt | 230 + vendor/dissimilar/benches/document2.txt | 188 + vendor/dissimilar/src/find.rs | 232 + vendor/dissimilar/src/lib.rs | 887 + vendor/dissimilar/src/range.rs | 148 + vendor/dissimilar/src/tests.rs | 580 + vendor/dissimilar/tests/test.rs | 19 + vendor/either/.cargo-checksum.json | 2 +- vendor/either/Cargo.toml | 2 +- vendor/either/README.rst | 7 +- vendor/either/src/lib.rs | 118 + vendor/expect-test/.cargo-checksum.json | 2 +- vendor/expect-test/Cargo.toml | 6 +- vendor/expect-test/README.md | 8 + vendor/expect-test/src/lib.rs | 36 +- vendor/filetime/.cargo-checksum.json | 2 +- vendor/filetime/Cargo.toml | 6 +- vendor/filetime/src/unix/mod.rs | 11 +- vendor/flate2/.cargo-checksum.json | 2 +- vendor/flate2/Cargo.lock | 97 +- vendor/flate2/Cargo.toml | 17 +- vendor/flate2/README.md | 10 +- vendor/flate2/src/deflate/bufread.rs | 8 +- vendor/flate2/src/ffi/c.rs | 8 - vendor/flate2/src/ffi/rust.rs | 14 +- vendor/flate2/src/gz/bufread.rs | 6 +- vendor/flate2/src/lib.rs | 8 +- vendor/flate2/src/mem.rs | 87 + vendor/flate2/src/zlib/bufread.rs | 8 +- vendor/form_urlencoded/.cargo-checksum.json | 1 + vendor/form_urlencoded/Cargo.toml | 27 + vendor/form_urlencoded/LICENSE-APACHE | 201 + vendor/form_urlencoded/LICENSE-MIT | 25 + .../src/lib.rs} | 36 +- .../src/query_encoding.rs | 0 vendor/fs_extra/.cargo-checksum.json | 2 +- vendor/fs_extra/Cargo.toml | 4 +- vendor/fs_extra/README.md | 61 +- vendor/fs_extra/src/dir.rs | 386 +- vendor/fs_extra/src/error.rs | 11 +- vendor/fs_extra/src/file.rs | 20 +- vendor/fs_extra/src/lib.rs | 357 +- vendor/fs_extra/tests/dir.rs | 853 +- vendor/fs_extra/tests/file.rs | 260 +- vendor/fs_extra/tests/lib.rs | 619 +- vendor/heck/.cargo-checksum.json | 2 +- vendor/heck/Cargo.toml | 5 +- vendor/heck/README.md | 2 +- vendor/heck/src/camel.rs | 14 +- vendor/heck/src/kebab.rs | 14 +- vendor/heck/src/lib.rs | 4 +- vendor/heck/src/mixed.rs | 18 +- vendor/heck/src/shouty_kebab.rs | 51 + vendor/heck/src/shouty_snake.rs | 14 +- vendor/heck/src/snake.rs | 16 +- vendor/heck/src/title.rs | 14 +- vendor/hermit-abi/.cargo-checksum.json | 2 +- vendor/hermit-abi/Cargo.toml | 2 +- vendor/hermit-abi/src/lib.rs | 3 +- vendor/indexmap/.cargo-checksum.json | 2 +- vendor/indexmap/Cargo.toml | 7 +- vendor/indexmap/README.rst | 22 +- vendor/indexmap/src/lib.rs | 9 +- vendor/indexmap/src/map.rs | 59 + vendor/indexmap/src/map/core.rs | 210 +- vendor/indexmap/src/map/core/raw.rs | 263 +- vendor/indexmap/src/serde.rs | 22 +- vendor/indexmap/src/serde_seq.rs | 112 + vendor/indexmap/src/set.rs | 50 +- vendor/instant/.cargo-checksum.json | 2 +- vendor/instant/Cargo.toml | 9 +- vendor/instant/README.md | 20 + vendor/instant/src/lib.rs | 43 +- vendor/instant/src/wasm.rs | 25 +- vendor/instant/tests/wasm.rs | 3 + .../.cargo-checksum.json | 0 .../CHANGELOG.md | 0 .../{itertools => itertools-0.9.0}/Cargo.lock | 0 .../{itertools => itertools-0.9.0}/Cargo.toml | 0 vendor/itertools-0.9.0/LICENSE-APACHE | 201 + .../LICENSE-MIT | 0 .../{itertools => itertools-0.9.0}/README.rst | 0 .../benches/bench1.rs | 0 .../benches/combinations_with_replacement.rs | 0 .../benches/extra/mod.rs | 0 .../benches/extra/zipslices.rs | 0 .../benches/fold_specialization.rs | 0 .../benches/tree_fold1.rs | 0 .../benches/tuple_combinations.rs | 0 .../benches/tuples.rs | 0 .../examples/iris.data | 0 .../examples/iris.rs | 0 .../src/adaptors/mod.rs | 0 .../src/adaptors/multi_product.rs | 0 .../src/combinations.rs | 0 .../src/combinations_with_replacement.rs | 0 .../src/concat_impl.rs | 0 .../src/cons_tuples_impl.rs | 0 .../src/diff.rs | 0 .../src/either_or_both.rs | 0 .../src/exactly_one_err.rs | 0 .../src/format.rs | 0 .../src/free.rs | 0 .../src/group_map.rs | 0 .../src/groupbylazy.rs | 0 .../src/impl_macros.rs | 0 .../src/intersperse.rs | 0 .../src/kmerge_impl.rs | 0 .../src/lazy_buffer.rs | 0 .../{itertools => itertools-0.9.0}/src/lib.rs | 0 .../src/merge_join.rs | 0 .../src/minmax.rs | 0 .../src/multipeek_impl.rs | 0 .../src/pad_tail.rs | 0 .../src/peeking_take_while.rs | 0 .../src/permutations.rs | 0 .../src/process_results_impl.rs | 0 .../src/put_back_n_impl.rs | 0 .../src/rciter_impl.rs | 0 .../src/repeatn.rs | 0 .../src/size_hint.rs | 0 .../src/sources.rs | 0 .../{itertools => itertools-0.9.0}/src/tee.rs | 0 .../src/tuple_impl.rs | 0 .../src/unique_impl.rs | 0 .../src/with_position.rs | 0 .../src/zip_eq_impl.rs | 0 .../src/zip_longest.rs | 0 .../src/ziptuple.rs | 0 .../tests/adaptors_no_collect.rs | 0 .../tests/fold_specialization.rs | 0 .../tests/merge_join.rs | 0 .../tests/peeking_take_while.rs | 0 .../tests/quick.rs | 0 .../tests/specializations.rs | 0 .../tests/test_core.rs | 0 .../tests/test_std.rs | 0 .../tests/tuples.rs | 0 .../tests/zip.rs | 0 vendor/itoa/.cargo-checksum.json | 2 +- vendor/itoa/Cargo.toml | 4 +- vendor/itoa/benches/bench.rs | 2 +- vendor/itoa/src/lib.rs | 4 +- vendor/itoa/tests/test.rs | 7 +- vendor/libc/.cargo-checksum.json | 2 +- vendor/libc/CONTRIBUTING.md | 22 +- vendor/libc/Cargo.toml | 13 +- vendor/libc/README.md | 2 +- vendor/libc/src/fuchsia/mod.rs | 28 +- vendor/libc/src/lib.rs | 7 +- vendor/libc/src/psp.rs | 32 +- .../src/unix/bsd/apple/b64/aarch64/align.rs | 48 + .../src/unix/bsd/apple/b64/aarch64/mod.rs | 8 + vendor/libc/src/unix/bsd/apple/b64/mod.rs | 114 +- .../src/unix/bsd/apple/b64/x86_64/align.rs | 7 + .../libc/src/unix/bsd/apple/b64/x86_64/mod.rs | 112 + vendor/libc/src/unix/bsd/apple/mod.rs | 38 +- .../src/unix/bsd/freebsdlike/dragonfly/mod.rs | 21 + .../bsd/freebsdlike/freebsd/freebsd11/mod.rs | 3 +- .../bsd/freebsdlike/freebsd/freebsd12/mod.rs | 24 +- .../bsd/freebsdlike/freebsd/freebsd13/b64.rs | 34 + .../bsd/freebsdlike/freebsd/freebsd13/mod.rs | 250 + .../src/unix/bsd/freebsdlike/freebsd/mod.rs | 38 +- vendor/libc/src/unix/bsd/freebsdlike/mod.rs | 102 +- vendor/libc/src/unix/bsd/mod.rs | 17 +- .../src/unix/bsd/netbsdlike/netbsd/mod.rs | 79 + .../src/unix/bsd/netbsdlike/openbsd/mod.rs | 83 +- vendor/libc/src/unix/haiku/mod.rs | 11 + .../src/unix/linux_like/android/b32/mod.rs | 2 +- .../linux_like/android/b64/aarch64/mod.rs | 43 + .../src/unix/linux_like/android/b64/mod.rs | 31 +- .../libc/src/unix/linux_like/android/mod.rs | 19 + .../src/unix/linux_like/emscripten/mod.rs | 6 + .../libc/src/unix/linux_like/linux/align.rs | 23 + .../unix/linux_like/linux/gnu/b32/arm/mod.rs | 2 +- .../src/unix/linux_like/linux/gnu/b32/mod.rs | 1 - .../unix/linux_like/linux/gnu/b32/powerpc.rs | 2 +- .../linux_like/linux/gnu/b32/riscv32/mod.rs | 2 +- .../linux_like/linux/gnu/b32/sparc/mod.rs | 2 +- .../unix/linux_like/linux/gnu/b32/x86/mod.rs | 2 +- .../linux_like/linux/gnu/b64/aarch64/ilp32.rs | 62 + .../linux_like/linux/gnu/b64/aarch64/lp64.rs | 71 + .../linux_like/linux/gnu/b64/aarch64/mod.rs | 76 +- .../linux_like/linux/gnu/b64/mips64/mod.rs | 1 - .../src/unix/linux_like/linux/gnu/b64/mod.rs | 14 +- .../linux_like/linux/gnu/b64/powerpc64/mod.rs | 3 +- .../linux_like/linux/gnu/b64/riscv64/mod.rs | 2 +- .../unix/linux_like/linux/gnu/b64/s390x.rs | 1 - .../linux_like/linux/gnu/b64/sparc64/mod.rs | 3 +- .../linux_like/linux/gnu/b64/x86_64/mod.rs | 2 +- .../libc/src/unix/linux_like/linux/gnu/mod.rs | 195 +- vendor/libc/src/unix/linux_like/linux/mod.rs | 104 + .../unix/linux_like/linux/musl/b32/arm/mod.rs | 20 +- .../linux_like/linux/musl/b32/mips/mod.rs | 18 - .../unix/linux_like/linux/musl/b32/x86/mod.rs | 20 +- .../linux/musl/b64/aarch64/align.rs | 12 +- .../unix/linux_like/linux/musl/b64/mips64.rs | 18 - .../linux_like/linux/musl/b64/powerpc64.rs | 2 +- .../linux_like/linux/musl/b64/x86_64/mod.rs | 20 +- .../src/unix/linux_like/linux/musl/mod.rs | 51 +- vendor/libc/src/unix/linux_like/mod.rs | 6 - vendor/libc/src/unix/mod.rs | 100 +- vendor/libc/src/unix/newlib/mod.rs | 21 +- vendor/libc/src/unix/redox/mod.rs | 46 +- vendor/libc/src/unix/solarish/illumos.rs | 5 + vendor/libc/src/unix/solarish/mod.rs | 47 +- vendor/libc/src/unix/solarish/solaris.rs | 4 + vendor/libc/src/unix/uclibc/mips/mod.rs | 2 + vendor/libc/src/unix/uclibc/mod.rs | 85 + vendor/libc/src/vxworks/mod.rs | 4 + vendor/libc/triagebot.toml | 6 - vendor/lock_api/.cargo-checksum.json | 2 +- vendor/lock_api/Cargo.toml | 2 +- vendor/lock_api/src/lib.rs | 2 + vendor/lock_api/src/mutex.rs | 2 +- vendor/lock_api/src/rwlock.rs | 8 +- vendor/log/.cargo-checksum.json | 2 +- vendor/log/CHANGELOG.md | 31 +- vendor/log/Cargo.toml | 31 +- vendor/log/benches/value.rs | 30 + vendor/log/build.rs | 33 +- vendor/log/src/kv/key.rs | 33 + vendor/log/src/kv/mod.rs | 2 +- vendor/log/src/kv/source.rs | 419 +- vendor/log/src/kv/value.rs | 655 + vendor/log/src/kv/value/fill.rs | 164 - vendor/log/src/kv/value/impls.rs | 159 - vendor/log/src/kv/value/internal/cast.rs | 475 - vendor/log/src/kv/value/internal/fmt.rs | 249 - vendor/log/src/kv/value/internal/mod.rs | 181 - vendor/log/src/kv/value/internal/sval.rs | 210 - vendor/log/src/kv/value/mod.rs | 56 - vendor/log/src/kv/value/test.rs | 81 - vendor/log/src/lib.rs | 144 +- vendor/log/src/macros.rs | 32 +- vendor/log/src/serde.rs | 71 + vendor/log/tests/filters.rs | 72 - vendor/log/tests/macros.rs | 36 - vendor/mdbook/.cargo-checksum.json | 2 +- vendor/mdbook/CHANGELOG.md | 44 + vendor/mdbook/Cargo.lock | 2 +- vendor/mdbook/Cargo.toml | 2 +- vendor/mdbook/README.md | 6 +- vendor/mdbook/src/book/book.rs | 52 +- vendor/mdbook/src/book/init.rs | 11 +- vendor/mdbook/src/book/mod.rs | 18 +- vendor/mdbook/src/cmd/init.rs | 12 +- vendor/mdbook/src/config.rs | 7 +- vendor/mdbook/src/lib.rs | 4 +- vendor/mdbook/src/preprocess/links.rs | 7 +- .../renderer/html_handlebars/hbs_renderer.rs | 8 +- .../renderer/html_handlebars/helpers/toc.rs | 20 +- .../src/renderer/html_handlebars/search.rs | 2 + vendor/mdbook/src/renderer/mod.rs | 49 +- vendor/mdbook/src/theme/searcher/searcher.js | 3 +- vendor/mdbook/src/utils/fs.rs | 5 +- vendor/mdbook/src/utils/string.rs | 6 +- vendor/mdbook/src/utils/toml_ext.rs | 2 +- vendor/mdbook/tests/alternative_backends.rs | 52 +- vendor/mdbook/tests/init.rs | 40 + vendor/mdbook/tests/rendered_output.rs | 5 +- vendor/mdbook/tests/searchindex_fixture.json | 168 +- vendor/memchr/.cargo-checksum.json | 2 +- vendor/memchr/Cargo.toml | 2 +- vendor/memchr/build.rs | 17 +- .../memchr/src/tests/x86_64-soft_float.json | 15 + vendor/memoffset-0.5.5/.cargo-checksum.json | 1 + vendor/memoffset-0.5.5/Cargo.toml | 31 + vendor/memoffset-0.5.5/LICENSE | 19 + vendor/memoffset-0.5.5/README.md | 88 + vendor/memoffset-0.5.5/build.rs | 16 + vendor/memoffset-0.5.5/ci/miri.sh | 14 + vendor/memoffset-0.5.5/src/lib.rs | 91 + vendor/memoffset-0.5.5/src/offset_of.rs | 197 + vendor/memoffset-0.5.5/src/raw_field.rs | 84 + vendor/memoffset-0.5.5/src/span_of.rs | 263 + vendor/memoffset/.cargo-checksum.json | 2 +- vendor/memoffset/Cargo.toml | 2 +- vendor/memoffset/README.md | 18 +- vendor/memoffset/build.rs | 3 + vendor/memoffset/ci/miri.sh | 4 +- vendor/memoffset/src/lib.rs | 3 +- vendor/memoffset/src/offset_of.rs | 85 +- vendor/memoffset/src/raw_field.rs | 31 + vendor/miniz_oxide/.cargo-checksum.json | 2 +- vendor/miniz_oxide/Cargo.toml | 9 +- vendor/miniz_oxide/LICENSE-APACHE.md | 177 + vendor/miniz_oxide/LICENSE-MIT.md | 21 + vendor/miniz_oxide/LICENSE-ZLIB.md | 11 + vendor/miniz_oxide/build.rs | 6 + vendor/miniz_oxide/src/inflate/mod.rs | 4 +- vendor/miniz_oxide/src/inflate/stream.rs | 2 +- vendor/miniz_oxide/src/lib.rs | 7 +- vendor/num-integer/.cargo-checksum.json | 2 +- vendor/num-integer/Cargo.toml | 4 +- vendor/num-integer/README.md | 16 +- vendor/num-integer/RELEASES.md | 10 + vendor/num-integer/build.rs | 9 +- vendor/num-traits/.cargo-checksum.json | 2 +- vendor/num-traits/Cargo.toml | 4 +- vendor/num-traits/README.md | 15 + vendor/num-traits/RELEASES.md | 27 + vendor/num-traits/build.rs | 10 +- vendor/num-traits/src/cast.rs | 76 +- vendor/num-traits/src/lib.rs | 14 +- vendor/num-traits/src/ops/mod.rs | 1 + vendor/num-traits/src/ops/overflowing.rs | 104 + vendor/num-traits/src/sign.rs | 13 +- vendor/num-traits/tests/cast.rs | 3 +- .../.cargo-checksum.json | 0 vendor/{object => object-0.22.0}/Cargo.lock | 0 vendor/{object => object-0.22.0}/Cargo.toml | 0 vendor/object-0.22.0/LICENSE-APACHE | 201 + vendor/{object => object-0.22.0}/LICENSE-MIT | 0 vendor/{object => object-0.22.0}/README.md | 0 .../{object => object-0.22.0}/examples/ar.rs | 0 .../{object => object-0.22.0}/examples/nm.rs | 0 .../examples/objcopy.rs | 0 .../examples/objdump.rs | 0 .../examples/objectmap.rs | 0 .../{object => object-0.22.0}/src/archive.rs | 0 .../{object => object-0.22.0}/src/common.rs | 0 vendor/{object => object-0.22.0}/src/elf.rs | 0 .../{object => object-0.22.0}/src/endian.rs | 0 vendor/{object => object-0.22.0}/src/lib.rs | 0 vendor/{object => object-0.22.0}/src/macho.rs | 0 vendor/{object => object-0.22.0}/src/pe.rs | 0 vendor/{object => object-0.22.0}/src/pod.rs | 0 .../{object => object-0.22.0}/src/read/any.rs | 0 .../src/read/archive.rs | 0 .../src/read/coff/comdat.rs | 0 .../src/read/coff/file.rs | 0 .../src/read/coff/mod.rs | 0 .../src/read/coff/relocation.rs | 0 .../src/read/coff/section.rs | 0 .../src/read/coff/symbol.rs | 0 .../src/read/elf/comdat.rs | 0 .../src/read/elf/compression.rs | 0 .../src/read/elf/file.rs | 0 .../src/read/elf/mod.rs | 0 .../src/read/elf/note.rs | 0 .../src/read/elf/relocation.rs | 0 .../src/read/elf/section.rs | 0 .../src/read/elf/segment.rs | 0 .../src/read/elf/symbol.rs | 0 .../src/read/macho/file.rs | 0 .../src/read/macho/load_command.rs | 0 .../src/read/macho/mod.rs | 0 .../src/read/macho/relocation.rs | 0 .../src/read/macho/section.rs | 0 .../src/read/macho/segment.rs | 0 .../src/read/macho/symbol.rs | 0 .../{object => object-0.22.0}/src/read/mod.rs | 0 .../src/read/pe/file.rs | 0 .../src/read/pe/mod.rs | 0 .../src/read/pe/section.rs | 0 .../src/read/traits.rs | 0 .../src/read/util.rs | 0 .../src/read/wasm.rs | 0 .../src/write/coff.rs | 0 .../src/write/elf.rs | 0 .../src/write/macho.rs | 0 .../src/write/mod.rs | 0 .../src/write/string.rs | 0 .../src/write/util.rs | 0 .../tests/integration.rs | 0 .../tests/parse_self.rs | 0 .../tests/round_trip/bss.rs | 0 .../tests/round_trip/comdat.rs | 0 .../tests/round_trip/common.rs | 0 .../tests/round_trip/elf.rs | 0 .../tests/round_trip/mod.rs | 0 .../tests/round_trip/tls.rs | 0 vendor/once_cell/.cargo-checksum.json | 2 +- vendor/once_cell/CHANGELOG.md | 17 + vendor/once_cell/Cargo.lock | 77 +- vendor/once_cell/Cargo.toml | 8 +- vendor/once_cell/bors.toml | 2 + vendor/once_cell/src/imp_std.rs | 6 - vendor/once_cell/src/lib.rs | 515 +- vendor/once_cell/src/race.rs | 256 + vendor/once_cell/tests/{test.rs => it.rs} | 301 +- vendor/parking_lot/.cargo-checksum.json | 2 +- vendor/parking_lot/CHANGELOG.md | 10 + vendor/parking_lot/Cargo.toml | 2 +- vendor/parking_lot/src/condvar.rs | 3 + vendor/parking_lot/src/raw_rwlock.rs | 6 +- vendor/parking_lot/src/rwlock.rs | 11 +- vendor/parking_lot_core/.cargo-checksum.json | 2 +- vendor/parking_lot_core/Cargo.toml | 8 +- vendor/parking_lot_core/src/lib.rs | 4 - vendor/parking_lot_core/src/parking_lot.rs | 51 +- .../src/thread_parker/cloudabi.rs | 305 - .../parking_lot_core/src/thread_parker/mod.rs | 3 - .../src/thread_parker/unix.rs | 12 +- .../src/thread_parker/wasm_atomic.rs | 36 +- vendor/pin-project-lite/.cargo-checksum.json | 1 + vendor/pin-project-lite/CHANGELOG.md | 158 + vendor/pin-project-lite/Cargo.toml | 35 + vendor/pin-project-lite/LICENSE-APACHE | 177 + vendor/pin-project-lite/LICENSE-MIT | 23 + vendor/pin-project-lite/README.md | 128 + vendor/pin-project-lite/src/lib.rs | 1737 ++ .../pin-project-lite/tests/auxiliary/mod.rs | 14 + vendor/pin-project-lite/tests/compiletest.rs | 15 + vendor/pin-project-lite/tests/drop_order.rs | 168 + .../pin-project-lite/tests/include/basic.rs | 35 + vendor/pin-project-lite/tests/lint.rs | 262 + vendor/pin-project-lite/tests/proper_unpin.rs | 66 + vendor/pin-project-lite/tests/test.rs | 618 + .../tests/ui/conflict-drop.rs | 15 + .../tests/ui/conflict-drop.stderr | 16 + .../tests/ui/conflict-unpin.rs | 40 + .../tests/ui/conflict-unpin.stderr | 50 + .../tests/ui/invalid-bounds.rs | 93 + .../tests/ui/invalid-bounds.stderr | 176 + vendor/pin-project-lite/tests/ui/invalid.rs | 25 + .../pin-project-lite/tests/ui/invalid.stderr | 32 + .../tests/ui/overlapping_lifetime_names.rs | 10 + .../ui/overlapping_lifetime_names.stderr | 75 + .../tests/ui/overlapping_unpin_struct.rs | 19 + .../tests/ui/overlapping_unpin_struct.stderr | 11 + vendor/pin-project-lite/tests/ui/packed.rs | 19 + .../pin-project-lite/tests/ui/packed.stderr | 55 + .../pin-project-lite/tests/ui/unpin_sneaky.rs | 12 + .../tests/ui/unpin_sneaky.stderr | 11 + .../pin-project-lite/tests/ui/unsupported.rs | 27 + .../tests/ui/unsupported.stderr | 53 + vendor/quote/.cargo-checksum.json | 2 +- vendor/quote/Cargo.toml | 4 +- vendor/quote/src/lib.rs | 4 +- vendor/quote/src/runtime.rs | 18 +- vendor/quote/tests/test.rs | 28 +- vendor/quote/tests/ui/not-repeatable.rs | 5 +- vendor/rayon-core/.cargo-checksum.json | 2 +- vendor/rayon-core/Cargo.toml | 8 +- vendor/rayon-core/README.md | 2 +- vendor/rayon-core/src/join/test.rs | 18 + vendor/rayon-core/src/lib.rs | 2 +- vendor/rayon-core/src/sleep/counters.rs | 8 +- vendor/rayon/.cargo-checksum.json | 2 +- vendor/rayon/Cargo.lock | 156 +- vendor/rayon/Cargo.toml | 6 +- vendor/rayon/README.md | 6 +- vendor/rayon/RELEASES.md | 31 + vendor/rayon/src/collections/binary_heap.rs | 62 + vendor/rayon/src/collections/hash_map.rs | 29 + vendor/rayon/src/collections/hash_set.rs | 27 + vendor/rayon/src/collections/mod.rs | 54 + vendor/rayon/src/collections/vec_deque.rs | 84 +- vendor/rayon/src/compile_fail/must_use.rs | 3 + vendor/rayon/src/iter/copied.rs | 8 +- vendor/rayon/src/iter/flat_map.rs | 12 +- vendor/rayon/src/iter/flat_map_iter.rs | 147 + vendor/rayon/src/iter/flatten.rs | 122 +- vendor/rayon/src/iter/flatten_iter.rs | 132 + vendor/rayon/src/iter/mod.rs | 232 +- vendor/rayon/src/iter/positions.rs | 137 + vendor/rayon/src/lib.rs | 3 +- vendor/rayon/src/math.rs | 26 + vendor/rayon/src/prelude.rs | 2 + vendor/rayon/src/string.rs | 48 + vendor/rayon/src/vec.rs | 154 +- vendor/rayon/tests/clones.rs | 3 + vendor/rayon/tests/debug.rs | 18 +- vendor/rayon/tests/octillion.rs | 9 + .../redox_syscall-0.1.57/.cargo-checksum.json | 1 + vendor/redox_syscall-0.1.57/Cargo.toml | 23 + vendor/redox_syscall-0.1.57/LICENSE | 22 + vendor/redox_syscall-0.1.57/README.md | 6 + .../redox_syscall-0.1.57/src/arch/aarch64.rs | 77 + vendor/redox_syscall-0.1.57/src/arch/arm.rs | 73 + .../redox_syscall-0.1.57/src/arch/nonredox.rs | 31 + vendor/redox_syscall-0.1.57/src/arch/x86.rs | 73 + .../redox_syscall-0.1.57/src/arch/x86_64.rs | 74 + vendor/redox_syscall-0.1.57/src/call.rs | 393 + vendor/redox_syscall-0.1.57/src/data.rs | 309 + vendor/redox_syscall-0.1.57/src/error.rs | 311 + vendor/redox_syscall-0.1.57/src/flag.rs | 169 + vendor/redox_syscall-0.1.57/src/io/dma.rs | 76 + vendor/redox_syscall-0.1.57/src/io/io.rs | 67 + vendor/redox_syscall-0.1.57/src/io/mmio.rs | 31 + vendor/redox_syscall-0.1.57/src/io/mod.rs | 11 + vendor/redox_syscall-0.1.57/src/io/pio.rs | 89 + vendor/redox_syscall-0.1.57/src/lib.rs | 59 + vendor/redox_syscall-0.1.57/src/number.rs | 76 + .../src/scheme/generate.sh | 20 + vendor/redox_syscall-0.1.57/src/scheme/mod.rs | 9 + .../redox_syscall-0.1.57/src/scheme/scheme.rs | 167 + .../src/scheme/scheme_block.rs | 167 + .../src/scheme/scheme_block_mut.rs | 167 + .../src/scheme/scheme_mut.rs | 167 + vendor/redox_syscall-0.1.57/src/tests.rs | 129 + vendor/redox_syscall/.cargo-checksum.json | 2 +- vendor/redox_syscall/Cargo.toml | 5 +- vendor/redox_syscall/src/arch/aarch64.rs | 15 - vendor/redox_syscall/src/arch/arm.rs | 11 - vendor/redox_syscall/src/arch/nonredox.rs | 5 - vendor/redox_syscall/src/arch/x86.rs | 11 - vendor/redox_syscall/src/arch/x86_64.rs | 168 +- vendor/redox_syscall/src/call.rs | 81 +- vendor/redox_syscall/src/data.rs | 186 +- vendor/redox_syscall/src/error.rs | 2 +- vendor/redox_syscall/src/flag.rs | 250 +- vendor/redox_syscall/src/io/dma.rs | 182 +- vendor/redox_syscall/src/io/mmio.rs | 26 +- vendor/redox_syscall/src/lib.rs | 13 +- vendor/redox_syscall/src/number.rs | 44 +- vendor/redox_syscall/src/scheme/generate.sh | 5 +- vendor/redox_syscall/src/scheme/mod.rs | 2 + vendor/redox_syscall/src/scheme/scheme.rs | 45 +- .../redox_syscall/src/scheme/scheme_block.rs | 45 +- .../src/scheme/scheme_block_mut.rs | 45 +- vendor/redox_syscall/src/scheme/scheme_mut.rs | 45 +- vendor/redox_syscall/src/scheme/seek.rs | 33 + vendor/redox_syscall/src/tests.rs | 372 +- vendor/regex-syntax/.cargo-checksum.json | 2 +- vendor/regex-syntax/Cargo.toml | 2 +- vendor/regex-syntax/src/ast/parse.rs | 48 +- vendor/regex-syntax/src/hir/translate.rs | 24 +- vendor/regex-syntax/src/lib.rs | 3 +- vendor/regex-syntax/src/unicode.rs | 12 +- vendor/regex-syntax/src/utf8.rs | 4 + vendor/regex/.cargo-checksum.json | 2 +- vendor/regex/CHANGELOG.md | 70 +- vendor/regex/Cargo.lock | 18 +- vendor/regex/Cargo.toml | 4 +- .../examples/shootout-regex-dna-bytes.rs | 2 +- .../examples/shootout-regex-dna-cheat.rs | 2 +- .../shootout-regex-dna-single-cheat.rs | 2 +- .../examples/shootout-regex-dna-single.rs | 2 +- vendor/regex/examples/shootout-regex-dna.rs | 2 +- vendor/regex/src/compile.rs | 13 +- vendor/regex/src/dfa.rs | 12 +- vendor/regex/src/exec.rs | 5 + vendor/regex/src/expand.rs | 50 +- vendor/regex/src/lib.rs | 9 +- vendor/regex/src/literal/imp.rs | 3 +- vendor/regex/src/pikevm.rs | 2 +- vendor/regex/src/re_builder.rs | 2 + vendor/regex/src/re_bytes.rs | 48 +- vendor/regex/src/re_set.rs | 25 +- vendor/regex/src/re_trait.rs | 26 +- vendor/regex/src/re_unicode.rs | 50 +- vendor/regex/tests/api.rs | 12 + vendor/regex/tests/regression_fuzz.rs | 19 + vendor/regex/tests/set.rs | 11 + vendor/regex/tests/test_default.rs | 1 + vendor/regex/tests/unicode.rs | 3 + vendor/rls-data/.cargo-checksum.json | 2 +- vendor/rls-data/Cargo.toml | 10 +- vendor/rls-data/src/config.rs | 3 + vendor/rls-data/src/lib.rs | 81 +- vendor/rls-data/src/serde_expanded.rs | 9397 ------- vendor/rls-span/.cargo-checksum.json | 2 +- vendor/rls-span/Cargo.toml | 8 +- vendor/rls-span/src/compiler.rs | 5 +- vendor/rls-span/src/lib.rs | 145 +- vendor/rls-span/src/serde_expanded.rs | 2689 -- vendor/serde/.cargo-checksum.json | 2 +- vendor/serde/Cargo.toml | 4 +- vendor/serde/build.rs | 5 + vendor/serde/src/de/from_primitive.rs | 260 - vendor/serde/src/de/ignored_any.rs | 16 + vendor/serde/src/de/impls.rs | 448 +- vendor/serde/src/de/mod.rs | 3 +- vendor/serde/src/de/seed.rs | 19 + vendor/serde/src/de/value.rs | 192 +- vendor/serde/src/export.rs | 39 - vendor/serde/src/integer128.rs | 2 +- vendor/serde/src/lib.rs | 25 +- vendor/serde/src/macros.rs | 2 +- vendor/serde/src/private/de.rs | 164 +- .../serde/src/private/{macros.rs => doc.rs} | 47 +- vendor/serde/src/private/mod.rs | 49 +- vendor/serde/src/private/ser.rs | 28 - vendor/serde/src/private/size_hint.rs | 21 + vendor/serde/src/ser/impossible.rs | 2 +- vendor/serde/src/ser/mod.rs | 2 +- vendor/serde_derive/.cargo-checksum.json | 2 +- vendor/serde_derive/Cargo.toml | 5 +- vendor/serde_derive/src/bound.rs | 97 +- vendor/serde_derive/src/de.rs | 644 +- vendor/serde_derive/src/internals/attr.rs | 123 +- vendor/serde_derive/src/internals/case.rs | 61 +- vendor/serde_derive/src/internals/mod.rs | 4 + vendor/serde_derive/src/internals/receiver.rs | 287 + vendor/serde_derive/src/internals/respan.rs | 16 + vendor/serde_derive/src/internals/symbol.rs | 1 + vendor/serde_derive/src/lib.rs | 11 +- vendor/serde_derive/src/pretend.rs | 8 +- vendor/serde_derive/src/ser.rs | 54 +- vendor/serde_derive/src/try.rs | 6 +- vendor/serde_json/.cargo-checksum.json | 2 +- vendor/serde_json/Cargo.toml | 2 +- vendor/serde_json/src/lib.rs | 5 +- vendor/serde_json/src/value/from.rs | 36 + vendor/serde_json/src/value/mod.rs | 4 +- vendor/sharded-slab/.cargo-checksum.json | 2 +- vendor/sharded-slab/CHANGELOG.md | 39 + vendor/sharded-slab/CONTRIBUTING.md | 22 - vendor/sharded-slab/Cargo.toml | 10 +- vendor/sharded-slab/README.md | 6 +- vendor/sharded-slab/bin/loom.sh | 12 +- vendor/sharded-slab/src/cfg.rs | 41 +- vendor/sharded-slab/src/clear.rs | 7 + vendor/sharded-slab/src/iter.rs | 15 +- vendor/sharded-slab/src/lib.rs | 557 +- vendor/sharded-slab/src/page/mod.rs | 67 +- vendor/sharded-slab/src/page/slot.rs | 468 +- vendor/sharded-slab/src/page/stack.rs | 2 +- vendor/sharded-slab/src/pool.rs | 1342 + vendor/sharded-slab/src/pool/mod.rs | 325 - vendor/sharded-slab/src/pool/tests.rs | 182 - vendor/sharded-slab/src/shard.rs | 245 +- vendor/sharded-slab/src/sync.rs | 98 +- vendor/sharded-slab/src/tests/loom_pool.rs | 641 + .../src/{tests.rs => tests/loom_slab.rs} | 287 +- vendor/sharded-slab/src/tests/mod.rs | 71 + vendor/sharded-slab/src/tid.rs | 20 +- vendor/sharded-slab/tests/tests.rs | 27 - vendor/smallvec/.cargo-checksum.json | 2 +- vendor/smallvec/Cargo.toml | 2 +- vendor/smallvec/src/lib.rs | 111 +- vendor/smallvec/src/tests.rs | 13 + vendor/socket2/.cargo-checksum.json | 2 +- vendor/socket2/9f0fbf2.diff | 2623 -- vendor/socket2/Cargo.toml | 10 +- vendor/socket2/SO_ACCEPTCONN.patch | 96 + vendor/socket2/TODO | 343 +- vendor/socket2/TODO.refactor | 7 - vendor/socket2/diff.patch | 134 + vendor/socket2/socket_type.diff | 39 - vendor/socket2/src/sockaddr.rs | 4 +- vendor/socket2/src/socket.rs | 69 + vendor/socket2/src/sys/unix.rs | 97 +- vendor/syn/.cargo-checksum.json | 2 +- vendor/syn/Cargo.toml | 5 +- vendor/syn/build.rs | 4 + vendor/syn/src/attr.rs | 26 +- vendor/syn/src/custom_keyword.rs | 46 +- vendor/syn/src/custom_punctuation.rs | 36 +- vendor/syn/src/data.rs | 32 +- vendor/syn/src/derive.rs | 12 +- vendor/syn/src/error.rs | 46 +- vendor/syn/src/export.rs | 2 + vendor/syn/src/expr.rs | 181 +- vendor/syn/src/file.rs | 37 +- vendor/syn/src/gen/clone.rs | 183 + vendor/syn/src/gen/debug.rs | 178 + vendor/syn/src/gen/eq.rs | 358 + vendor/syn/src/gen/hash.rs | 176 + vendor/syn/src/generics.rs | 89 +- vendor/syn/src/group.rs | 21 +- vendor/syn/src/ident.rs | 1 + vendor/syn/src/item.rs | 313 +- vendor/syn/src/lib.rs | 49 +- vendor/syn/src/lifetime.rs | 5 +- vendor/syn/src/lit.rs | 37 +- vendor/syn/src/mac.rs | 6 + vendor/syn/src/macros.rs | 17 +- vendor/syn/src/op.rs | 6 + vendor/syn/src/parse.rs | 59 +- vendor/syn/src/parse_macro_input.rs | 13 +- vendor/syn/src/parse_quote.rs | 5 +- vendor/syn/src/pat.rs | 85 +- vendor/syn/src/path.rs | 82 +- vendor/syn/src/punctuated.rs | 26 +- vendor/syn/src/reserved.rs | 2 + vendor/syn/src/spanned.rs | 4 +- vendor/syn/src/stmt.rs | 11 +- vendor/syn/src/token.rs | 40 +- vendor/syn/src/ty.rs | 143 +- vendor/syn/tests/common/eq.rs | 328 +- vendor/syn/tests/debug/gen.rs | 24 +- vendor/syn/tests/repo/mod.rs | 47 +- vendor/syn/tests/test_item.rs | 200 +- vendor/syn/tests/test_precedence.rs | 31 +- vendor/syn/tests/test_round_trip.rs | 220 +- vendor/syn/tests/test_stmt.rs | 30 + vendor/syn/tests/test_ty.rs | 166 + vendor/syn/tests/test_visibility.rs | 5 +- vendor/termcolor/.cargo-checksum.json | 2 +- vendor/termcolor/Cargo.toml | 6 +- vendor/termcolor/src/lib.rs | 49 +- vendor/thread_local/.cargo-checksum.json | 2 +- vendor/thread_local/Cargo.toml | 20 +- vendor/thread_local/README.md | 18 +- vendor/thread_local/benches/thread_local.rs | 36 +- vendor/thread_local/src/cached.rs | 103 +- vendor/thread_local/src/lib.rs | 556 +- vendor/thread_local/src/thread_id.rs | 114 +- vendor/thread_local/src/unreachable.rs | 35 +- vendor/tinyvec/.cargo-checksum.json | 2 +- vendor/tinyvec/CHANGELOG.md | 34 + vendor/tinyvec/Cargo.toml | 33 +- vendor/tinyvec/LICENSE-APACHE.md | 67 + vendor/tinyvec/LICENSE-MIT.md | 5 + vendor/tinyvec/LICENSE-ZLIB.md | 22 +- vendor/tinyvec/README.md | 33 +- vendor/tinyvec/appveyor.yml | 32 - vendor/tinyvec/benches/macros.rs | 100 +- vendor/tinyvec/gen-array-impls.sh | 53 + vendor/tinyvec/rustfmt.toml | 24 +- vendor/tinyvec/src-backup/arrayset.rs | 303 + vendor/tinyvec/src/array.rs | 128 +- .../tinyvec/src/array/const_generic_impl.rs | 23 + vendor/tinyvec/src/array/generated_impl.rs | 9616 ++++++++ vendor/tinyvec/src/arrayvec.rs | 2836 ++- vendor/tinyvec/src/arrayvec_drain.rs | 93 + vendor/tinyvec/src/lib.rs | 207 +- vendor/tinyvec/src/slicevec.rs | 1026 + vendor/tinyvec/src/tinyvec.rs | 2580 +- vendor/tinyvec/tests/arrayvec.rs | 625 +- vendor/tinyvec/tests/tinyvec.rs | 480 +- vendor/tinyvec_macros/.cargo-checksum.json | 1 + .../{cloudabi => tinyvec_macros}/Cargo.toml | 22 +- vendor/tinyvec_macros/LICENSE | 21 + vendor/tinyvec_macros/src/lib.rs | 24 + .../tracing-subscriber/.cargo-checksum.json | 2 +- vendor/tracing-subscriber/CHANGELOG.md | 34 + vendor/tracing-subscriber/Cargo.toml | 8 +- vendor/tracing-subscriber/README.md | 2 +- vendor/tracing-subscriber/benches/enter.rs | 64 + vendor/tracing-subscriber/benches/fmt.rs | 87 +- .../tracing-subscriber/src/fmt/fmt_layer.rs | 30 +- .../tracing-subscriber/src/fmt/format/mod.rs | 35 +- .../src/fmt/format/pretty.rs | 339 + vendor/tracing-subscriber/src/fmt/mod.rs | 170 +- vendor/tracing-subscriber/src/lib.rs | 8 +- .../src/registry/extensions.rs | 129 +- .../src/registry/sharded.rs | 183 +- .../tracing-subscriber/src/registry/stack.rs | 31 +- vendor/tracing-subscriber/src/sync.rs | 5 + vendor/tracing-tree/.cargo-checksum.json | 2 +- vendor/tracing-tree/Cargo.lock | 258 +- vendor/tracing-tree/Cargo.toml | 24 +- vendor/tracing-tree/examples/basic.rs | 10 + vendor/tracing-tree/examples/basic.stdout | 1 + vendor/tracing-tree/examples/quiet.rs | 79 + vendor/tracing-tree/examples/quiet.stdout | 28 + vendor/tracing-tree/examples/stderr.stderr | 137 +- .../tracing-tree/examples/wraparound.stdout | 77 +- vendor/tracing-tree/src/format.rs | 50 +- vendor/tracing-tree/src/lib.rs | 127 +- vendor/tracing/.cargo-checksum.json | 2 +- vendor/tracing/CHANGELOG.md | 53 +- vendor/tracing/Cargo.toml | 15 +- vendor/tracing/README.md | 64 +- vendor/tracing/src/dispatcher.rs | 4 +- vendor/tracing/src/instrument.rs | 194 + vendor/tracing/src/lib.rs | 155 +- vendor/tracing/src/macros.rs | 257 +- vendor/tracing/src/span.rs | 76 +- vendor/tracing/tests/event.rs | 14 + .../filter_caching_is_lexically_scoped.rs | 1 + ...s_are_not_reevaluated_for_the_same_span.rs | 1 + ...re_reevaluated_for_different_call_sites.rs | 1 + vendor/tracing/tests/filters_dont_leak.rs | 82 + vendor/tracing/tests/macro_imports.rs | 2 + vendor/tracing/tests/macros.rs | 69 + vendor/tracing/tests/max_level_hint.rs | 1 + .../tracing/tests/multiple_max_level_hints.rs | 3 + vendor/tracing/tests/span.rs | 37 + vendor/tracing/tests/subscriber.rs | 1 + vendor/tracing/tests/support/subscriber.rs | 184 +- .../.cargo-checksum.json | 2 +- vendor/unicode-normalization/Cargo.toml | 6 +- vendor/unicode-normalization/README.md | 2 +- .../unicode-normalization/scripts/unicode.py | 4 +- vendor/unicode-normalization/src/decompose.rs | 8 +- .../src/no_std_prelude.rs | 0 .../unicode-normalization/src/stream_safe.rs | 9 +- vendor/unicode-normalization/src/tables.rs | 20207 ++++++++-------- .../unicode-segmentation/.cargo-checksum.json | 2 +- vendor/unicode-segmentation/Cargo.toml | 11 +- vendor/unicode-segmentation/README.md | 14 +- .../unicode-segmentation/benches/graphemes.rs | 64 + .../unicode-segmentation/scripts/unicode.py | 25 +- .../scripts/unicode_gen_breaktests.py | 0 vendor/unicode-segmentation/src/grapheme.rs | 71 +- vendor/unicode-segmentation/src/lib.rs | 48 +- vendor/unicode-segmentation/src/sentence.rs | 22 +- vendor/unicode-segmentation/src/tables.rs | 1703 +- vendor/unicode-segmentation/src/testdata.rs | 6 +- vendor/unicode-segmentation/src/word.rs | 30 +- vendor/url/.cargo-checksum.json | 2 +- vendor/url/Cargo.toml | 23 +- vendor/url/README.md | 10 - vendor/url/UPGRADING.md | 367 - vendor/url/appveyor.yml | 13 - vendor/url/benches/parse_url.rs | 18 - vendor/url/src/host.rs | 119 +- vendor/url/src/lib.rs | 134 +- vendor/url/src/origin.rs | 10 +- vendor/url/src/parser.rs | 145 +- vendor/url/src/path_segments.rs | 16 +- vendor/url/src/quirks.rs | 28 +- vendor/url/src/slicing.rs | 2 +- vendor/url/tests/data.rs | 210 - vendor/url/tests/setters_tests.json | 1866 -- vendor/url/tests/unit.rs | 621 - vendor/url/tests/urltestdata.json | 6686 ----- version | 2 +- x.py | 16 + 4664 files changed, 150208 insertions(+), 97647 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/panic.rs create mode 100644 compiler/rustc_codegen_cranelift/crate_patches/0001-compiler-builtins-Remove-rotate_left-from-Int.patch create mode 100644 compiler/rustc_codegen_cranelift/scripts/ext_config.sh create mode 100644 compiler/rustc_error_codes/src/error_codes/E0521.md create mode 100644 compiler/rustc_error_codes/src/error_codes/E0780.md create mode 100644 compiler/rustc_error_codes/src/error_codes/E0781.md create mode 100644 compiler/rustc_lint/src/non_fmt_panic.rs delete mode 100644 compiler/rustc_lint/src/panic_fmt.rs create mode 100644 compiler/rustc_middle/src/mir/graph_cyclic_cache.rs create mode 100644 compiler/rustc_mir/src/transform/inline/cycle.rs create mode 100644 compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs create mode 100644 compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs create mode 100644 compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs create mode 100644 compiler/rustc_target/src/spec/i386_unknown_linux_gnu.rs create mode 100644 compiler/rustc_target/src/spec/i486_unknown_linux_gnu.rs create mode 100644 compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs create mode 100644 compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs create mode 100644 compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs create mode 100644 library/alloc/src/vec/cow.rs create mode 100644 library/alloc/src/vec/drain.rs create mode 100644 library/alloc/src/vec/drain_filter.rs create mode 100644 library/alloc/src/vec/in_place_drop.rs create mode 100644 library/alloc/src/vec/into_iter.rs create mode 100644 library/alloc/src/vec/is_zero.rs rename library/alloc/src/{vec.rs => vec/mod.rs} (65%) create mode 100644 library/alloc/src/vec/partial_eq.rs create mode 100644 library/alloc/src/vec/set_len_on_drop.rs create mode 100644 library/alloc/src/vec/source_iter_marker.rs create mode 100644 library/alloc/src/vec/spec_extend.rs create mode 100644 library/alloc/src/vec/spec_from_elem.rs create mode 100644 library/alloc/src/vec/spec_from_iter.rs create mode 100644 library/alloc/src/vec/spec_from_iter_nested.rs create mode 100644 library/alloc/src/vec/splice.rs create mode 100644 library/core/src/iter/adapters/intersperse.rs create mode 100644 library/core/src/stream/mod.rs create mode 100644 library/core/src/stream/stream/mod.rs create mode 100644 library/core/tests/const_ptr.rs delete mode 100644 library/core/tests/iter.rs create mode 100644 library/core/tests/iter/adapters/chain.rs create mode 100644 library/core/tests/iter/adapters/cloned.rs create mode 100644 library/core/tests/iter/adapters/copied.rs create mode 100644 library/core/tests/iter/adapters/cycle.rs create mode 100644 library/core/tests/iter/adapters/enumerate.rs create mode 100644 library/core/tests/iter/adapters/filter.rs create mode 100644 library/core/tests/iter/adapters/filter_map.rs create mode 100644 library/core/tests/iter/adapters/flat_map.rs create mode 100644 library/core/tests/iter/adapters/flatten.rs create mode 100644 library/core/tests/iter/adapters/fuse.rs create mode 100644 library/core/tests/iter/adapters/inspect.rs create mode 100644 library/core/tests/iter/adapters/intersperse.rs create mode 100644 library/core/tests/iter/adapters/map.rs create mode 100644 library/core/tests/iter/adapters/mod.rs create mode 100644 library/core/tests/iter/adapters/peekable.rs create mode 100644 library/core/tests/iter/adapters/scan.rs create mode 100644 library/core/tests/iter/adapters/skip.rs create mode 100644 library/core/tests/iter/adapters/skip_while.rs create mode 100644 library/core/tests/iter/adapters/step_by.rs create mode 100644 library/core/tests/iter/adapters/take.rs create mode 100644 library/core/tests/iter/adapters/take_while.rs create mode 100644 library/core/tests/iter/adapters/zip.rs create mode 100644 library/core/tests/iter/mod.rs create mode 100644 library/core/tests/iter/range.rs create mode 100644 library/core/tests/iter/sources.rs create mode 100644 library/core/tests/iter/traits/accum.rs create mode 100644 library/core/tests/iter/traits/double_ended.rs create mode 100644 library/core/tests/iter/traits/iterator.rs create mode 100644 library/core/tests/iter/traits/mod.rs create mode 100644 library/core/tests/iter/traits/step.rs create mode 100644 library/core/tests/num/ops.rs delete mode 100644 library/std/src/future.rs delete mode 100644 src/doc/book/listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/output.txt create mode 100644 src/doc/nomicon/src/arc-base.md create mode 100644 src/doc/nomicon/src/arc-clone.md create mode 100644 src/doc/nomicon/src/arc-drop.md create mode 100644 src/doc/nomicon/src/arc-final.md create mode 100644 src/doc/nomicon/src/arc-layout.md create mode 100644 src/doc/nomicon/src/arc.md create mode 100644 src/doc/reference/src/names.md create mode 100644 src/doc/reference/src/names/name-resolution.md create mode 100644 src/doc/reference/src/names/namespaces.md create mode 100644 src/doc/reference/src/names/preludes.md create mode 100644 src/doc/reference/src/names/scopes.md create mode 100644 src/doc/rustc/src/lints/listing/deny-by-default.md create mode 100644 src/doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md delete mode 100644 src/doc/unstable-book/src/language-features/const-in-array-repeat-expressions.md create mode 100644 src/doc/unstable-book/src/language-features/intra-doc-pointers.md rename src/{test/rustdoc-json => etc}/check_missing_items.py (99%) delete mode 100644 src/librustdoc/passes/collapse_docs.rs create mode 100644 src/rustdoc-json-types/Cargo.toml create mode 100644 src/rustdoc-json-types/README.md rename src/{librustdoc/json/types.rs => rustdoc-json-types/lib.rs} (95%) create mode 100644 src/test/codegen/abi-repr-ext.rs delete mode 100644 src/test/codegen/intrinsics/move-val-init.rs create mode 100644 src/test/codegen/issue-59352.rs create mode 100644 src/test/codegen/slice-as_chunks.rs create mode 100644 src/test/codegen/slice-ref-equality.rs create mode 100644 src/test/codegen/vecdeque_no_panic.rs delete mode 100644 src/test/compile-fail/auxiliary/panic-runtime-lang-items.rs delete mode 100644 src/test/compile-fail/auxiliary/panic-runtime-unwind.rs delete mode 100644 src/test/compile-fail/auxiliary/panic-runtime-unwind2.rs delete mode 100644 src/test/compile-fail/auxiliary/some-panic-impl.rs delete mode 100644 src/test/compile-fail/auxiliary/wants-panic-runtime-unwind.rs delete mode 100644 src/test/compile-fail/runtime-depend-on-needs-runtime.stderr create mode 100644 src/test/incremental/issue-80336-invalid-span.rs rename src/test/mir-opt/{const_promotion_extern_static.BAR-promoted[0].ConstProp.after.mir => const_promotion_extern_static.BAR-promoted[0].SimplifyCfg-elaborate-drops.after.mir} (92%) rename src/test/mir-opt/{const_promotion_extern_static.FOO-promoted[0].ConstProp.after.mir => const_promotion_extern_static.FOO-promoted[0].SimplifyCfg-elaborate-drops.after.mir} (95%) create mode 100644 src/test/mir-opt/inline/cycle.f.Inline.diff create mode 100644 src/test/mir-opt/inline/cycle.g.Inline.diff create mode 100644 src/test/mir-opt/inline/cycle.main.Inline.diff create mode 100644 src/test/mir-opt/inline/cycle.rs create mode 100644 src/test/mir-opt/inline/inline-cycle-generic.rs create mode 100644 src/test/mir-opt/inline/inline-instruction-set.rs create mode 100644 src/test/mir-opt/inline/inline_cycle_generic.main.Inline.diff create mode 100644 src/test/mir-opt/inline/inline_instruction_set.default.Inline.diff create mode 100644 src/test/mir-opt/inline/inline_instruction_set.t32.Inline.diff delete mode 100644 src/test/mir-opt/inst_combine_deref.rs create mode 100644 src/test/mir-opt/issues/issue-59352.rs create mode 100644 src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir create mode 100644 src/test/pretty/expanded-and-path-remap-80832.pp create mode 100644 src/test/pretty/expanded-and-path-remap-80832.rs create mode 100644 src/test/run-make-fulldeps/split-debuginfo/Makefile create mode 100644 src/test/run-make-fulldeps/split-debuginfo/foo.rs create mode 100644 src/test/run-make/const_fn_mir/Makefile create mode 100644 src/test/run-make/const_fn_mir/dump.mir create mode 100644 src/test/run-make/const_fn_mir/main.rs create mode 100644 src/test/rustdoc-js-std/primitive.js create mode 100644 src/test/rustdoc-js/primitive.js create mode 100644 src/test/rustdoc-js/primitive.rs delete mode 100644 src/test/rustdoc-json/compare.py delete mode 100644 src/test/rustdoc-json/nested.expected delete mode 100644 src/test/rustdoc-json/structs.expected delete mode 100644 src/test/rustdoc-json/structs.rs create mode 100644 src/test/rustdoc-json/structs/plain_empty.rs create mode 100644 src/test/rustdoc-json/structs/tuple.rs create mode 100644 src/test/rustdoc-json/structs/unit.rs create mode 100644 src/test/rustdoc-json/structs/with_generics.rs create mode 100644 src/test/rustdoc-json/structs/with_primitives.rs create mode 100644 src/test/rustdoc-json/traits/has_body.rs create mode 100644 src/test/rustdoc-json/unions/union.rs create mode 100644 src/test/rustdoc-ui/auxiliary/issue-61592.rs create mode 100644 src/test/rustdoc-ui/deref-generic.rs create mode 100644 src/test/rustdoc-ui/deref-recursive-cycle.rs create mode 100644 src/test/rustdoc-ui/doc-alias-same-name.rs create mode 100644 src/test/rustdoc-ui/doc-alias-same-name.stderr create mode 100644 src/test/rustdoc-ui/ignore-block-help.rs create mode 100644 src/test/rustdoc-ui/ignore-block-help.stderr create mode 100644 src/test/rustdoc-ui/intra-doc/auxiliary/pointer-reexports-allowed.rs create mode 100644 src/test/rustdoc-ui/intra-doc/feature-gate-intra-doc-pointers.rs create mode 100644 src/test/rustdoc-ui/intra-doc/feature-gate-intra-doc-pointers.stderr create mode 100644 src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.rs create mode 100644 src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr create mode 100644 src/test/rustdoc-ui/intra-doc/non-path-primitives.rs create mode 100644 src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr create mode 100644 src/test/rustdoc-ui/intra-doc/pointer-reexports-allowed.rs create mode 100644 src/test/rustdoc-ui/intra-doc/unused-extern-crate.rs create mode 100644 src/test/rustdoc-ui/intra-doc/unused-extern-crate.stderr create mode 100644 src/test/rustdoc-ui/issue-61592-2.rs create mode 100644 src/test/rustdoc-ui/issue-61592-2.stderr create mode 100644 src/test/rustdoc-ui/issue-61592.rs create mode 100644 src/test/rustdoc-ui/issue-61592.stderr create mode 100644 src/test/rustdoc-ui/issue-80992.rs create mode 100644 src/test/rustdoc-ui/issue-80992.stdout create mode 100644 src/test/rustdoc-ui/issue-81662-shortness.rs create mode 100644 src/test/rustdoc-ui/issue-81662-shortness.stdout create mode 100644 src/test/rustdoc-ui/range-pattern.rs create mode 100644 src/test/rustdoc-ui/reference-link-reports-error-once.rs create mode 100644 src/test/rustdoc-ui/reference-link-reports-error-once.stderr create mode 100644 src/test/rustdoc-ui/reference-links.rs create mode 100644 src/test/rustdoc-ui/reference-links.stderr create mode 100644 src/test/rustdoc-ui/run-directory.correct.stdout create mode 100644 src/test/rustdoc-ui/run-directory.incorrect.stdout create mode 100644 src/test/rustdoc-ui/run-directory.rs create mode 100644 src/test/rustdoc/auxiliary/issue-61592.rs create mode 100644 src/test/rustdoc/auxiliary/macro_pub_in_module.rs create mode 100644 src/test/rustdoc/deref-recursive-pathbuf.rs create mode 100644 src/test/rustdoc/deref-recursive.rs create mode 100644 src/test/rustdoc/fn-type.rs create mode 100644 src/test/rustdoc/for-lifetime.rs create mode 100644 src/test/rustdoc/implementor-stable-version.rs create mode 100644 src/test/rustdoc/intra-doc/non-path-primitives.rs create mode 100644 src/test/rustdoc/intra-doc/primitive-disambiguators.rs create mode 100644 src/test/rustdoc/issue-61592.rs create mode 100644 src/test/rustdoc/issue-80233-normalize-auto-trait.rs create mode 100644 src/test/rustdoc/macro_pub_in_module.rs create mode 100644 src/test/rustdoc/mut-params.rs create mode 100644 src/test/rustdoc/range-arg-pattern.rs rename src/test/ui/{issues => abi}/issue-28676.rs (57%) create mode 100644 src/test/ui/allocator/object-safe.rs rename src/test/ui/{ => array-slice-vec}/array-break-length.rs (100%) rename src/test/ui/{ => array-slice-vec}/array-break-length.stderr (100%) rename src/test/ui/{ => array-slice-vec}/array-not-vector.rs (100%) rename src/test/ui/{ => array-slice-vec}/array-not-vector.stderr (100%) rename src/test/ui/{ => array-slice-vec}/array_const_index-0.rs (71%) rename src/test/ui/{ => array-slice-vec}/array_const_index-0.stderr (59%) rename src/test/ui/{ => array-slice-vec}/array_const_index-1.stderr (59%) create mode 100644 src/test/ui/array-slice-vec/array_const_index-2.rs rename src/test/ui/{issues => array-slice-vec}/issue-15730.rs (100%) rename src/test/ui/{issues => array-slice-vec}/issue-18425.rs (100%) create mode 100644 src/test/ui/array-slice-vec/repeat_empty_ok.rs create mode 100644 src/test/ui/array-slice-vec/repeat_empty_ok.stderr rename src/test/ui/{ => array-slice-vec}/slice-2.rs (100%) rename src/test/ui/{ => array-slice-vec}/slice-2.stderr (100%) rename src/test/ui/{ => array-slice-vec}/slice-mut-2.rs (100%) rename src/test/ui/{ => array-slice-vec}/slice-mut-2.stderr (100%) rename src/test/ui/{ => array-slice-vec}/slice-mut.rs (100%) rename src/test/ui/{ => array-slice-vec}/slice-mut.stderr (100%) rename src/test/ui/{ => array-slice-vec}/slice-to-vec-comparison.rs (100%) rename src/test/ui/{ => array-slice-vec}/slice-to-vec-comparison.stderr (100%) rename src/test/ui/{vec => array-slice-vec}/vec-macro-with-comma-only.rs (100%) rename src/test/ui/{vec => array-slice-vec}/vec-macro-with-comma-only.stderr (100%) rename src/test/ui/{vec => array-slice-vec}/vec-mut-iter-borrow.rs (100%) rename src/test/ui/{vec => array-slice-vec}/vec-mut-iter-borrow.stderr (100%) rename src/test/ui/{vec => array-slice-vec}/vec-overrun.rs (100%) rename src/test/ui/{vec => array-slice-vec}/vec-res-add.rs (100%) rename src/test/ui/{vec => array-slice-vec}/vec-res-add.stderr (100%) rename src/test/ui/{ => array-slice-vec}/vector-cast-weirdness.rs (100%) rename src/test/ui/{ => array-slice-vec}/vector-cast-weirdness.stderr (100%) rename src/test/ui/{ => array-slice-vec}/vector-no-ann.rs (100%) rename src/test/ui/{ => array-slice-vec}/vector-no-ann.stderr (100%) delete mode 100644 src/test/ui/array_const_index-1.rs create mode 100644 src/test/ui/associated-item/associated-item-two-bounds.rs rename src/test/ui/{issues => associated-types}/issue-18655.rs (100%) rename src/test/ui/{issues => associated-types}/issue-22037.rs (100%) rename src/test/ui/{issues => associated-types}/issue-22037.stderr (100%) rename src/test/{compile-fail => ui/associated-types}/issue-23595-1.rs (100%) create mode 100644 src/test/ui/associated-types/issue-23595-1.stderr create mode 100644 src/test/ui/associated-types/issue-24159.rs rename src/test/ui/{issues => associated-types}/issue-24338.rs (100%) rename src/test/{compile-fail => ui/associated-types}/issue-27675-unchecked-bounds.rs (100%) create mode 100644 src/test/ui/associated-types/issue-27675-unchecked-bounds.stderr create mode 100644 src/test/ui/associated-types/issue-37808.rs create mode 100644 src/test/ui/associated-types/issue-37883.rs create mode 100644 src/test/ui/associated-types/issue-38917.rs create mode 100644 src/test/ui/associated-types/issue-39532.rs create mode 100644 src/test/ui/associated-types/issue-40093.rs create mode 100644 src/test/ui/associated-types/issue-43475.rs rename src/test/ui/{issues => associated-types}/issue-48551.rs (100%) rename src/test/ui/{issues => associated-types}/issue-50301.rs (100%) create mode 100644 src/test/ui/associated-types/issue-63591.rs rename src/test/ui/{ => associated-types}/object-method-numbering.rs (100%) rename src/test/ui/{issues => async-await}/issue-73541-2.rs (100%) rename src/test/ui/{issues => async-await}/issue-73541-2.stderr (100%) rename src/test/ui/{issues => async-await}/issue-76547.nll.stderr (100%) rename src/test/ui/{issues => async-await}/issue-76547.rs (100%) rename src/test/ui/{issues => async-await}/issue-76547.stderr (100%) create mode 100644 src/test/ui/async-await/issues/issue-78938-async-block.rs create mode 100644 src/test/ui/async-await/issues/issue-78938-async-block.stderr create mode 100644 src/test/ui/attributes/key-value-expansion-on-mac.rs create mode 100644 src/test/ui/attributes/key-value-expansion-on-mac.stderr rename src/test/ui/{issues => autoref-autoderef}/issue-38940.rs (100%) rename src/test/ui/{issues => autoref-autoderef}/issue-38940.stderr (100%) delete mode 100644 src/test/ui/auxiliary/trait_superkinds_in_metadata.rs create mode 100644 src/test/ui/backtrace-apple-no-dsymutil.rs create mode 100644 src/test/ui/binding/const-param.full.stderr create mode 100644 src/test/ui/binding/const-param.min.stderr delete mode 100644 src/test/ui/binding/const-param.stderr rename src/test/ui/{ => binop}/binary-minus-without-space.rs (100%) rename src/test/ui/{ => binop}/binary-op-on-double-ref.fixed (100%) rename src/test/ui/{ => binop}/binary-op-on-double-ref.rs (100%) rename src/test/ui/{ => binop}/binary-op-on-double-ref.stderr (100%) rename src/test/ui/{ => binop}/binops-issue-22743.rs (100%) rename src/test/ui/{ => binop}/binops.rs (100%) rename src/test/ui/{issues => binop}/issue-25916.rs (100%) rename src/test/ui/{issues => binop}/issue-28837.rs (100%) rename src/test/ui/{issues => binop}/issue-28837.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-17263.rs (100%) rename src/test/ui/{issues => borrowck}/issue-17545.rs (100%) rename src/test/ui/{issues => borrowck}/issue-17545.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-20801.rs (100%) rename src/test/ui/{issues => borrowck}/issue-20801.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-24267-flow-exit.rs (100%) rename src/test/ui/{issues => borrowck}/issue-24267-flow-exit.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-25793.rs (100%) rename src/test/ui/{issues => borrowck}/issue-25793.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-27282-move-match-input-into-guard.rs (100%) rename src/test/ui/{issues => borrowck}/issue-27282-move-match-input-into-guard.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-27282-mutate-before-diverging-arm-2.rs (100%) rename src/test/ui/{issues => borrowck}/issue-27282-mutate-before-diverging-arm-2.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-27282-reborrow-ref-mut-in-guard.rs (100%) rename src/test/ui/{issues => borrowck}/issue-27282-reborrow-ref-mut-in-guard.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-33819.rs (100%) rename src/test/ui/{issues => borrowck}/issue-33819.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-45199.rs (100%) rename src/test/ui/{issues => borrowck}/issue-45199.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-46471.rs (100%) rename src/test/ui/{issues => borrowck}/issue-46471.stderr (100%) create mode 100644 src/test/ui/borrowck/move-in-pattern-mut-in-loop.rs create mode 100644 src/test/ui/borrowck/move-in-pattern-mut-in-loop.stderr rename src/test/ui/{ => builtin-superkinds}/builtin-superkinds-capabilities-transitive.rs (100%) rename src/test/ui/{ => builtin-superkinds}/builtin-superkinds-capabilities-xc.rs (100%) rename src/test/ui/{ => builtin-superkinds}/builtin-superkinds-capabilities.rs (100%) rename src/test/ui/{builtin-superkinds-in-metadata.rs => builtin-superkinds/builtin-superkinds-in-metadata2.rs} (100%) rename src/test/ui/{ => builtin-superkinds}/builtin-superkinds-phantom-typaram.rs (100%) rename src/test/ui/{builtin-superkinds-simple.rs => builtin-superkinds/builtin-superkinds-simple2.rs} (100%) rename src/test/ui/{ => builtin-superkinds}/builtin-superkinds-typaram.rs (100%) rename src/test/ui/{ => cast}/cast-char.rs (100%) rename src/test/ui/{ => cast}/cast-char.stderr (100%) rename src/test/ui/{ => cast}/cast-does-fallback.rs (100%) rename src/test/ui/{ => cast}/cast-region-to-uint.rs (100%) rename src/test/ui/{ => cast}/cast-rfc0401-vtable-kinds.rs (100%) rename src/test/ui/{ => cast}/cast-rfc0401.rs (100%) rename src/test/ui/{ => cast}/cast-to-infer-ty.rs (100%) rename src/test/ui/{ => cast}/cast.rs (100%) rename src/test/ui/{ => cast}/casts-differing-anon.rs (100%) rename src/test/ui/{ => cast}/casts-differing-anon.stderr (100%) rename src/test/ui/{ => cast}/casts-issue-46365.rs (100%) rename src/test/ui/{ => cast}/casts-issue-46365.stderr (100%) rename src/test/ui/{ => cast}/fat-ptr-cast-rpass.rs (100%) rename src/test/ui/{ => cast}/fat-ptr-cast.rs (100%) rename src/test/ui/{ => cast}/fat-ptr-cast.stderr (100%) rename src/test/ui/{issues => cast}/issue-17444.rs (100%) rename src/test/ui/{issues => cast}/issue-17444.stderr (100%) create mode 100644 src/test/ui/cast/unsupported-cast.rs rename src/test/ui/{ => cast}/unsupported-cast.stderr (65%) create mode 100644 src/test/ui/cfg/assume-incomplete-release/assume-incomplete.rs create mode 100644 src/test/ui/cfg/assume-incomplete-release/auxiliary/ver-cfg-rel.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/by_value.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/by_value.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/move_closure.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/move_closure.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/by_value.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/fru_syntax.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr create mode 100644 src/test/ui/closures/closure-no-fn-4.rs create mode 100644 src/test/ui/closures/closure-no-fn-4.stderr create mode 100644 src/test/ui/closures/closure-no-fn-5.rs create mode 100644 src/test/ui/closures/closure-no-fn-5.stderr rename src/test/{compile-fail => ui/closures}/coerce-unsafe-closure-to-unsafe-fn-ptr.rs (100%) create mode 100644 src/test/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.stderr rename src/test/{compile-fail => ui/closures}/coerce-unsafe-to-closure.rs (100%) create mode 100644 src/test/ui/closures/coerce-unsafe-to-closure.stderr create mode 100644 src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs create mode 100644 src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr create mode 100644 src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs create mode 100644 src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr create mode 100644 src/test/ui/closures/issue-80313-mutation-in-closure.rs create mode 100644 src/test/ui/closures/issue-80313-mutation-in-closure.stderr create mode 100644 src/test/ui/closures/issue-80313-mutation-in-move-closure.rs create mode 100644 src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr create mode 100644 src/test/ui/closures/issue-81700-mut-borrow.rs create mode 100644 src/test/ui/closures/issue-81700-mut-borrow.stderr create mode 100644 src/test/ui/closures/local-type-mix.rs create mode 100644 src/test/ui/closures/local-type-mix.stderr rename src/test/ui/{block-arg-call-as.rs => closures/old-closure-arg-call-as.rs} (100%) rename src/test/ui/{block-arg.rs => closures/old-closure-arg.rs} (100%) rename src/test/ui/{block-explicit-types.rs => closures/old-closure-explicit-types.rs} (100%) rename src/test/ui/{block-expr-precedence.rs => closures/old-closure-expr-precedence.rs} (100%) rename src/test/ui/{block-expr-precedence.stderr => closures/old-closure-expr-precedence.stderr} (82%) rename src/test/ui/{block-expression-remove-semicolon.fixed => closures/old-closure-expression-remove-semicolon.fixed} (100%) rename src/test/ui/{block-expression-remove-semicolon.rs => closures/old-closure-expression-remove-semicolon.rs} (100%) rename src/test/ui/{block-expression-remove-semicolon.stderr => closures/old-closure-expression-remove-semicolon.stderr} (85%) rename src/test/ui/{block-fn-coerce.rs => closures/old-closure-fn-coerce.rs} (100%) rename src/test/ui/{block-iter-1.rs => closures/old-closure-iter-1.rs} (100%) rename src/test/ui/{block-iter-2.rs => closures/old-closure-iter-2.rs} (100%) create mode 100644 src/test/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs create mode 100644 src/test/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr create mode 100644 src/test/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-registers.rs create mode 100644 src/test/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.rs create mode 100644 src/test/ui/cmse-nonsecure/cmse-nonsecure-call/params-on-stack.stderr create mode 100644 src/test/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.rs create mode 100644 src/test/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.stderr create mode 100644 src/test/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.rs create mode 100644 src/test/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.stderr rename src/test/ui/{ => cmse-nonsecure}/cmse-nonsecure-entry/gate_test.rs (100%) rename src/test/ui/{ => cmse-nonsecure}/cmse-nonsecure-entry/gate_test.stderr (100%) rename src/test/ui/{ => cmse-nonsecure}/cmse-nonsecure-entry/params-on-registers.rs (100%) rename src/test/ui/{ => cmse-nonsecure}/cmse-nonsecure-entry/params-on-stack.rs (100%) rename src/test/ui/{ => cmse-nonsecure}/cmse-nonsecure-entry/params-on-stack.stderr (100%) rename src/test/ui/{ => cmse-nonsecure}/cmse-nonsecure-entry/trustzone-only.rs (100%) rename src/test/ui/{ => cmse-nonsecure}/cmse-nonsecure-entry/trustzone-only.stderr (100%) rename src/test/ui/{ => cmse-nonsecure}/cmse-nonsecure-entry/wrong-abi.rs (100%) rename src/test/ui/{ => cmse-nonsecure}/cmse-nonsecure-entry/wrong-abi.stderr (100%) create mode 100644 src/test/ui/command/command-current-dir.rs create mode 100644 src/test/ui/command/command-setgroups.rs create mode 100644 src/test/ui/const-generics/arg-in-pat-1.rs create mode 100644 src/test/ui/const-generics/arg-in-pat-2.rs create mode 100644 src/test/ui/const-generics/arg-in-pat-3.rs delete mode 100644 src/test/ui/const-generics/const-param-in-trait-ungated.rs delete mode 100644 src/test/ui/const-generics/const-param-in-trait-ungated.stderr create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/elaborate-trait-pred.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/eval-privacy.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/eval-privacy.stderr create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/nested-abstract-consts-1.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/nested-abstract-consts-2.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/nested_uneval_unification-1.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/nested_uneval_unification-2.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/subexprs_are_const_evalutable.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/ty-alias-substitution.rs create mode 100644 src/test/ui/const-generics/issue-71202.rs rename src/test/ui/{issues => const-generics}/issue-73899.rs (100%) rename src/test/ui/{issues => const-generics}/issue-75763.rs (100%) delete mode 100644 src/test/ui/const-generics/issues/issue-60263.rs delete mode 100644 src/test/ui/const-generics/issues/issue-60263.stderr create mode 100644 src/test/ui/const-generics/issues/issue-80062.rs create mode 100644 src/test/ui/const-generics/issues/issue-80062.stderr create mode 100644 src/test/ui/const-generics/issues/issue-80375.rs create mode 100644 src/test/ui/const-generics/issues/issue-80375.stderr create mode 100644 src/test/ui/const-generics/late-bound-vars/in_closure.rs create mode 100644 src/test/ui/const-generics/late-bound-vars/simple.rs delete mode 100644 src/test/ui/const-generics/min-and-full-same-time.rs delete mode 100644 src/test/ui/const-generics/min-and-full-same-time.stderr delete mode 100644 src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs delete mode 100644 src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr create mode 100644 src/test/ui/const-generics/suggest_const_for_array.rs create mode 100644 src/test/ui/const-generics/suggest_const_for_array.stderr create mode 100644 src/test/ui/const-ptr/out_of_bounds_read.rs create mode 100644 src/test/ui/const-ptr/out_of_bounds_read.stderr create mode 100644 src/test/ui/const_evaluatable/needs_where_clause.rs create mode 100644 src/test/ui/const_evaluatable/needs_where_clause.stderr create mode 100644 src/test/ui/const_evaluatable/no_where_clause.rs create mode 100644 src/test/ui/const_evaluatable/no_where_clause.stderr create mode 100644 src/test/ui/const_prop/inline_spans_lint_attribute.rs rename src/test/ui/{ => consts}/check_const-feature-gated.rs (100%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/const-repeat.rs (100%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/fn-call-in-const.rs (73%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/fn-call-in-non-const.rs (85%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/fn-call-in-non-const.stderr (92%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/migrate-fail.rs (92%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/migrate-fail.stderr (93%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/migrate-pass.rs (98%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/nll-fail.rs (91%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/nll-fail.stderr (93%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/nll-pass.rs (98%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/run-pass.rs (81%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/trait-error.rs (77%) rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/trait-error.stderr (94%) rename src/test/{compile-fail => ui}/consts/const-fn-error.rs (100%) create mode 100644 src/test/ui/consts/const-fn-error.stderr delete mode 100644 src/test/ui/consts/const-mut-refs/const_mut_address_of.stderr delete mode 100644 src/test/ui/consts/const-mut-refs/const_mut_refs.stderr create mode 100644 src/test/ui/consts/const-mut-refs/mut_ref_in_final.rs create mode 100644 src/test/ui/consts/const-mut-refs/mut_ref_in_final.stderr create mode 100644 src/test/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.rs create mode 100644 src/test/ui/consts/const-mut-refs/mut_ref_in_final_dynamic_check.stderr rename src/test/ui/{ => consts}/const-suggest-feature.rs (100%) rename src/test/ui/{ => consts}/const-suggest-feature.stderr (100%) create mode 100644 src/test/ui/consts/intrinsic_without_const_stab.rs create mode 100644 src/test/ui/consts/intrinsic_without_const_stab.stderr create mode 100644 src/test/ui/consts/intrinsic_without_const_stab_fail.rs create mode 100644 src/test/ui/consts/intrinsic_without_const_stab_fail.stderr rename src/test/ui/{issues => consts}/issue-17458.rs (100%) rename src/test/ui/{issues => consts}/issue-17458.stderr (100%) rename src/test/ui/{issues => consts}/issue-17718-borrow-interior.rs (100%) rename src/test/ui/{issues => consts}/issue-17718-const-bad-values.rs (65%) rename src/test/ui/{issues => consts}/issue-17718-const-bad-values.stderr (65%) rename src/test/ui/{issues => consts}/issue-17718-const-borrow.rs (55%) create mode 100644 src/test/ui/consts/issue-17718-const-borrow.stderr rename src/test/ui/{issues => consts}/issue-21562.rs (100%) rename src/test/ui/{issues => consts}/issue-21721.rs (100%) rename src/test/ui/{issues => consts}/issue-25826.rs (100%) rename src/test/ui/{issues => consts}/issue-25826.stderr (100%) rename src/test/ui/{issues => consts}/issue-27890.rs (100%) rename src/test/ui/{issues => consts}/issue-28113.rs (100%) rename src/test/ui/{issues => consts}/issue-28113.stderr (100%) rename src/test/ui/{issues => consts}/issue-29914-2.rs (100%) rename src/test/ui/{issues => consts}/issue-29914-3.rs (100%) rename src/test/ui/{issues => consts}/issue-29914.rs (100%) rename src/test/ui/{issues => consts}/issue-29927-1.rs (100%) rename src/test/ui/{issues => consts}/issue-29927.rs (100%) rename src/test/ui/{issues => consts}/issue-32829-2.rs (100%) rename src/test/ui/{issues => consts}/issue-32829-2.stderr (100%) rename src/test/ui/{issues => consts}/issue-36163.rs (100%) rename src/test/ui/{issues => consts}/issue-36163.stderr (100%) rename src/test/ui/{issues => consts}/issue-43105.rs (100%) rename src/test/ui/{issues => consts}/issue-43105.stderr (100%) rename src/test/{compile-fail => ui/consts}/issue-44415.rs (100%) create mode 100644 src/test/ui/consts/issue-44415.stderr rename src/test/ui/{issues => consts}/issue-46553.rs (100%) rename src/test/ui/{issues => consts}/issue-47789.rs (100%) rename src/test/ui/{issues => consts}/issue-52023-array-size-pointer-cast.rs (100%) rename src/test/ui/{issues => consts}/issue-52023-array-size-pointer-cast.stderr (100%) rename src/test/ui/{issues => consts}/issue-54348.rs (100%) rename src/test/ui/{issues => consts}/issue-54348.stderr (100%) rename src/test/{compile-fail => ui}/consts/issue-55878.rs (94%) create mode 100644 src/test/ui/consts/issue-55878.stderr rename src/test/ui/{issues => consts}/issue-56762.rs (100%) rename src/test/ui/{issues => consts}/issue-56762.stderr (100%) rename src/test/ui/{issues => consts}/issue-58435-ice-with-assoc-const.rs (100%) rename src/test/ui/{issues => consts}/issue-6991.rs (100%) create mode 100644 src/test/ui/consts/issue-79690.rs create mode 100644 src/test/ui/consts/issue-79690.stderr delete mode 100644 src/test/ui/consts/projection_qualif.mut_refs.stderr delete mode 100644 src/test/ui/consts/projection_qualif.stock.stderr delete mode 100644 src/test/ui/consts/promoted_div_by_zero.rs delete mode 100644 src/test/ui/consts/read_from_static_mut_ref.rs delete mode 100644 src/test/ui/consts/read_from_static_mut_ref.stderr rename src/test/ui/{ => consts}/rustc-args-required-const.rs (100%) rename src/test/ui/{ => consts}/rustc-args-required-const.stderr (100%) create mode 100644 src/test/ui/consts/write_to_mut_ref_dest.mut_refs.stderr rename src/test/ui/consts/{projection_qualif.rs => write_to_mut_ref_dest.rs} (72%) create mode 100644 src/test/ui/consts/write_to_mut_ref_dest.stock.stderr create mode 100644 src/test/ui/consts/write_to_static_via_mut_ref.rs create mode 100644 src/test/ui/consts/write_to_static_via_mut_ref.stderr rename src/test/{compile-fail => ui/crate-loading}/auxiliary/crateresolve1-1.rs (100%) rename src/test/{compile-fail => ui/crate-loading}/auxiliary/crateresolve1-2.rs (100%) rename src/test/{compile-fail => ui/crate-loading}/auxiliary/crateresolve1-3.rs (100%) rename src/test/{compile-fail => ui/crate-loading}/crateresolve1.rs (87%) create mode 100644 src/test/ui/derives/derive-Debug-use-ufcs-struct.rs create mode 100644 src/test/ui/derives/derive-Debug-use-ufcs-tuple.rs create mode 100644 src/test/ui/doc-alias-same-name.rs create mode 100644 src/test/ui/doc-alias-same-name.stderr rename src/test/ui/{issues => drop}/auxiliary/issue-10028.rs (100%) rename src/test/ui/{issues => drop}/issue-10028.rs (100%) rename src/test/ui/{issues => drop}/issue-30018-nopanic.rs (100%) rename src/test/ui/{issues => dropck}/issue-28498-ugeh-with-lifetime-param.rs (93%) rename src/test/ui/{issues => dropck}/issue-28498-ugeh-with-trait-bound.rs (94%) rename src/test/ui/{issues => dropck}/issue-29844.rs (100%) create mode 100644 src/test/ui/emit-metadata-obj.rs rename src/test/ui/{issues => enum-discriminant}/issue-43398.rs (100%) rename src/test/ui/{issues => enum-discriminant}/issue-43398.stderr (100%) create mode 100644 src/test/ui/error-codes/E0040.fixed create mode 100644 src/test/ui/error-codes/E0435.fixed create mode 100644 src/test/ui/explicit/explicit-call-to-dtor.fixed create mode 100644 src/test/ui/explicit/explicit-call-to-supertrait-dtor.fixed rename src/test/{compile-fail => ui/extern-flag}/empty-extern-arg.rs (100%) create mode 100644 src/test/ui/extern-flag/empty-extern-arg.stderr rename src/test/ui/{issues => extern}/issue-10025.rs (100%) rename src/test/ui/{issues => extern}/issue-10763.rs (100%) rename src/test/ui/{issues => extern}/issue-10764-rpass.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/allow-features-empty.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/allow-features-empty.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/allow-features.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/allow-features.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/duplicate-features.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/duplicate-features.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/feature-gate-c_variadic.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/feature-gate-c_variadic.stderr (100%) delete mode 100644 src/test/ui/feature-gates/feature-gate-const_generics-ptr.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-const_generics_defaults.rs create mode 100644 src/test/ui/feature-gates/feature-gate-const_generics_defaults.stderr delete mode 100644 src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-const_refs_to_cell.rs create mode 100644 src/test/ui/feature-gates/feature-gate-edition_macro_pats.rs create mode 100644 src/test/ui/feature-gates/feature-gate-edition_macro_pats.stderr rename src/test/ui/{ => feature-gates}/feature-gate-inline_const.rs (100%) rename src/test/ui/{ => feature-gates}/feature-gate-inline_const.stderr (100%) rename src/test/ui/{ => feature-gates}/feature-gate-isa_attribute.rs (100%) rename src/test/ui/{ => feature-gates}/feature-gate-isa_attribute.stderr (100%) rename src/test/ui/{ => feature-gates}/feature-gate-optimize_attribute.rs (100%) rename src/test/ui/{ => feature-gates}/feature-gate-optimize_attribute.stderr (100%) create mode 100644 src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.rs create mode 100644 src/test/ui/feature-gates/feature-gate-relaxed_struct_unsize.stderr rename src/test/ui/{feature-gate => feature-gates}/feature-gate-static-nobundle-2.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/feature-gate-static-nobundle-2.stderr (100%) rename src/test/ui/{ => feature-gates}/feature-gated-feature-in-macro-arg.rs (100%) rename src/test/ui/{ => feature-gates}/feature-gated-feature-in-macro-arg.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-bench.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-bench.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-builtin-attrs-error.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-builtin-attrs-error.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-builtin-attrs.rs (99%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-builtin-attrs.stderr (99%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-deprecated.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-derive-2.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-derive-2.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-derive.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-derive.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-macro_escape.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-macro_escape.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-macro_use.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-macro_use.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-proc_macro_derive.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-proc_macro_derive.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-rustc_deprecated.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-rustc_deprecated.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-stable.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-stable.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-test.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-test.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-unstable.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-43106-gating-of-unstable.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-49983-see-issue-0.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/issue-49983-see-issue-0.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/rustc-private.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/rustc-private.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/stability-attribute-consistency.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/stability-attribute-consistency.stderr (100%) rename src/test/ui/{ => feature-gates}/trace_macros-gate.rs (100%) rename src/test/ui/{ => feature-gates}/trace_macros-gate.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/unknown-feature.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/unknown-feature.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/unstable-attribute-allow-issue-0.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/unstable-attribute-allow-issue-0.stderr (100%) create mode 100644 src/test/ui/fn/issue-80179.rs create mode 100644 src/test/ui/fn/issue-80179.stderr rename src/test/ui/{issues => for-loop-while}/issue-2216.rs (100%) rename src/test/ui/{issues => for-loop-while}/issue-69841.rs (100%) create mode 100644 src/test/ui/generator/auxiliary/metadata-sufficient-for-layout.rs create mode 100644 src/test/ui/generator/layout-error.rs create mode 100644 src/test/ui/generator/layout-error.stderr create mode 100644 src/test/ui/generator/metadata-sufficient-for-layout.rs create mode 100644 src/test/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.rs create mode 100644 src/test/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr create mode 100644 src/test/ui/generic-associated-types/gat-in-trait-path.rs create mode 100644 src/test/ui/generic-associated-types/gat-in-trait-path.stderr create mode 100644 src/test/ui/generic-associated-types/gat-trait-path-generic-type-arg.rs create mode 100644 src/test/ui/generic-associated-types/gat-trait-path-generic-type-arg.stderr create mode 100644 src/test/ui/generic-associated-types/gat-trait-path-missing-lifetime.rs create mode 100644 src/test/ui/generic-associated-types/gat-trait-path-missing-lifetime.stderr create mode 100644 src/test/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs create mode 100644 src/test/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr create mode 100644 src/test/ui/generic-associated-types/issue-67510-pass.rs create mode 100644 src/test/ui/generic-associated-types/issue-67510-pass.stderr create mode 100644 src/test/ui/generic-associated-types/issue-67510.rs create mode 100644 src/test/ui/generic-associated-types/issue-67510.stderr create mode 100644 src/test/ui/generic-associated-types/issue-68648-1.rs create mode 100644 src/test/ui/generic-associated-types/issue-68648-1.stderr create mode 100644 src/test/ui/generic-associated-types/issue-68648-2.rs create mode 100644 src/test/ui/generic-associated-types/issue-68648-2.stderr create mode 100644 src/test/ui/generic-associated-types/issue-68649-pass.rs create mode 100644 src/test/ui/generic-associated-types/issue-68649-pass.stderr create mode 100644 src/test/ui/generic-associated-types/issue-74684-1.rs create mode 100644 src/test/ui/generic-associated-types/issue-74684-1.stderr create mode 100644 src/test/ui/generic-associated-types/issue-74684-2.rs create mode 100644 src/test/ui/generic-associated-types/issue-74684-2.stderr create mode 100644 src/test/ui/generic-associated-types/issue-76535.rs create mode 100644 src/test/ui/generic-associated-types/issue-76535.stderr create mode 100644 src/test/ui/generic-associated-types/issue-79422.rs create mode 100644 src/test/ui/generic-associated-types/issue-79422.stderr create mode 100644 src/test/ui/generic-associated-types/issue-80433-reduced.rs create mode 100644 src/test/ui/generic-associated-types/issue-80433.rs create mode 100644 src/test/ui/generic-associated-types/issue-80433.stderr delete mode 100644 src/test/ui/generic-associated-types/parse/trait-path-unimplemented.rs delete mode 100644 src/test/ui/generic-associated-types/parse/trait-path-unimplemented.stderr create mode 100644 src/test/ui/generic-associated-types/variance_constraints.rs rename src/test/ui/{issues => generics}/issue-2936.rs (100%) create mode 100644 src/test/ui/generics/wrong-number-of-args.rs create mode 100644 src/test/ui/generics/wrong-number-of-args.stderr create mode 100644 src/test/ui/hello2021.rs create mode 100644 src/test/ui/hygiene/no_implicit_prelude-2021.rs create mode 100644 src/test/ui/hygiene/traits-in-scope.rs create mode 100644 src/test/ui/illegal-ufcs-drop.fixed rename src/test/ui/{ => impl-trait}/nested_impl_trait.rs (100%) rename src/test/ui/{ => impl-trait}/nested_impl_trait.stderr (100%) rename src/test/ui/{ => impl-trait}/point-to-type-err-cause-on-impl-trait-return.rs (100%) rename src/test/ui/{ => impl-trait}/point-to-type-err-cause-on-impl-trait-return.stderr (100%) rename src/test/ui/{issues => imports}/issue-26873-onefile.rs (100%) rename src/test/ui/{issues => imports}/issue-32119.rs (100%) rename src/test/ui/{issues => imports}/issue-32222.rs (100%) create mode 100644 src/test/ui/inference/cannot-infer-closure-circular.rs create mode 100644 src/test/ui/inference/cannot-infer-closure-circular.stderr create mode 100644 src/test/ui/inference/cannot-infer-partial-try-return.rs create mode 100644 src/test/ui/inference/cannot-infer-partial-try-return.stderr delete mode 100644 src/test/ui/intrinsics/intrinsic-move-val-cleanups.rs delete mode 100644 src/test/ui/intrinsics/intrinsic-move-val.rs delete mode 100644 src/test/ui/issues/auxiliary/issue-10031-aux.rs delete mode 100644 src/test/ui/issues/issue-10031.rs delete mode 100644 src/test/ui/issues/issue-15881-model-lexer-dotdotdot.rs delete mode 100644 src/test/ui/issues/issue-17718-const-borrow.stderr create mode 100644 src/test/ui/issues/issue-27433.fixed create mode 100644 src/test/ui/issues/issue-3521-2.fixed create mode 100644 src/test/ui/issues/issue-3521.fixed create mode 100644 src/test/ui/issues/issue-3668-2.fixed create mode 100644 src/test/ui/issues/issue-44239.fixed create mode 100644 src/test/ui/issues/issue-80512-param-reordering-with-defaults.rs create mode 100644 src/test/ui/issues/issue-80512-param-reordering-with-defaults.stderr create mode 100644 src/test/ui/issues/issue-80607.rs create mode 100644 src/test/ui/issues/issue-80607.stderr create mode 100644 src/test/ui/label/label_misspelled_2.rs create mode 100644 src/test/ui/label/label_misspelled_2.stderr create mode 100644 src/test/ui/lifetimes/issue-79187-2.nll.stderr create mode 100644 src/test/ui/lifetimes/issue-79187-2.rs create mode 100644 src/test/ui/lifetimes/issue-79187-2.stderr create mode 100644 src/test/ui/lifetimes/issue-79187.nll.stderr create mode 100644 src/test/ui/lifetimes/issue-79187.rs create mode 100644 src/test/ui/lifetimes/issue-79187.stderr rename src/test/{compile-fail => ui/linkage-attr}/invalid-link-args.rs (75%) rename src/test/{compile-fail => ui/linkage-attr}/issue-10755.rs (72%) create mode 100644 src/test/ui/lint/clashing-extern-fn-wasm.rs create mode 100644 src/test/ui/lint/dead-code/const-and-self.stderr rename src/test/ui/{ => lint}/expr_attr_paren_order.rs (100%) rename src/test/ui/{ => lint}/expr_attr_paren_order.stderr (100%) rename src/test/ui/{issues => lint}/issue-14309.rs (100%) rename src/test/ui/{issues => lint}/issue-14309.stderr (100%) rename src/test/ui/{issues => lint}/issue-17718-const-naming.rs (100%) rename src/test/ui/{issues => lint}/issue-17718-const-naming.stderr (100%) rename src/test/ui/{issues => lint}/issue-30302.rs (100%) rename src/test/ui/{issues => lint}/issue-30302.stderr (100%) rename src/test/ui/{issues => lint}/issue-34798.rs (100%) create mode 100644 src/test/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.rs create mode 100644 src/test/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.stderr rename src/test/{compile-fail => ui/lint}/must_use-in-stdlib-traits.rs (100%) create mode 100644 src/test/ui/lint/must_use-in-stdlib-traits.stderr create mode 100644 src/test/ui/lint/redundant-semicolon/item-stmt-semi.stderr create mode 100644 src/test/ui/lint/semicolon-in-expressions-from-macros/allow-semicolon-in-expressions-from-macros.rs create mode 100644 src/test/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.rs create mode 100644 src/test/ui/lint/semicolon-in-expressions-from-macros/semicolon-in-expressions-from-macros.stderr rename src/test/ui/{ => lint}/trivial_casts.rs (100%) rename src/test/ui/{ => lint}/trivial_casts.stderr (100%) rename src/test/{compile-fail => ui/llvm-asm}/asm-src-loc-codegen-units.rs (81%) rename src/test/{compile-fail => ui/llvm-asm}/asm-src-loc.rs (77%) rename src/test/ui/{ => llvm-asm}/inline-asm-bad-constraint.rs (100%) rename src/test/ui/{ => llvm-asm}/inline-asm-bad-constraint.stderr (100%) rename src/test/ui/{ => llvm-asm}/inline-asm-bad-operand.rs (100%) rename src/test/ui/{ => llvm-asm}/inline-asm-bad-operand.stderr (100%) rename src/test/ui/{issues => llvm-asm}/issue-14936.rs (100%) rename src/test/ui/{issues => llvm-asm}/issue-33264.rs (100%) rename src/test/ui/{issues => macros}/auxiliary/issue-40469.rs (100%) create mode 100644 src/test/ui/macros/edition-macro-pats.rs rename src/test/ui/{issues => macros}/issue-26322.rs (100%) rename src/test/ui/{issues => macros}/issue-40469.rs (100%) rename src/test/ui/{issues => macros}/issue-41803.rs (100%) rename src/test/ui/{issues => macros}/issue-5060.rs (100%) create mode 100644 src/test/ui/macros/issue-81006.rs create mode 100644 src/test/ui/macros/issue-81006.stderr rename src/test/ui/{issues => macros}/issue-8709.rs (100%) rename src/test/{compile-fail => ui/macros}/not-utf8.bin (100%) rename src/test/{compile-fail => ui/macros}/not-utf8.rs (100%) create mode 100644 src/test/ui/macros/not-utf8.stderr create mode 100644 src/test/ui/macros/vec-macro-in-pattern.rs create mode 100644 src/test/ui/macros/vec-macro-in-pattern.stderr create mode 100644 src/test/ui/meta/meta-expected-error-wrong-rev.a.stderr rename src/test/{compile-fail => ui/meta}/meta-expected-error-wrong-rev.rs (100%) create mode 100644 src/test/ui/methods/method-lookup-order.rs rename src/test/ui/{issues => mir}/issue-66851.rs (100%) create mode 100644 src/test/ui/mir/issue-80742.rs create mode 100644 src/test/ui/mir/issue-80742.stderr create mode 100644 src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.rs create mode 100644 src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.stderr create mode 100644 src/test/ui/mir/ssa-analysis-regression-50041.rs rename src/test/ui/{issues => never_type}/issue-10176.rs (100%) rename src/test/ui/{issues => never_type}/issue-10176.stderr (100%) rename src/test/{compile-fail => ui/never_type}/issue-52443.rs (100%) create mode 100644 src/test/ui/never_type/issue-52443.stderr rename src/test/ui/{issues => nll}/issue-45696-long-live-borrows-in-boxes.rs (100%) rename src/test/ui/{issues => nll}/issue-45696-scribble-on-boxed-borrow.rs (100%) rename src/test/ui/{issues => nll}/issue-45696-scribble-on-boxed-borrow.stderr (100%) rename src/test/ui/{issues => nll}/issue-46036.rs (100%) rename src/test/ui/{issues => nll}/issue-46036.stderr (100%) rename src/test/ui/{issues => nll}/issue-51345-2.rs (100%) rename src/test/ui/{issues => nll}/issue-51770.rs (100%) rename src/test/ui/{issues => nll}/issue-54943-3.rs (100%) rename src/test/ui/{issues => nll}/issue-55511.rs (100%) rename src/test/ui/{issues => nll}/issue-55511.stderr (100%) delete mode 100644 src/test/ui/non-built-in-quote.rs rename src/test/ui/{panic-brace.rs => non-fmt-panic.rs} (70%) rename src/test/ui/{panic-brace.stderr => non-fmt-panic.stderr} (53%) rename src/test/ui/{issues => numbers-arithmetic}/issue-8460-const.noopt.stderr (100%) rename src/test/ui/{issues => numbers-arithmetic}/issue-8460-const.opt.stderr (100%) rename src/test/ui/{issues => numbers-arithmetic}/issue-8460-const.opt_with_overflow_checks.stderr (100%) rename src/test/ui/{issues => numbers-arithmetic}/issue-8460-const.rs (100%) rename src/test/ui/{issues => numbers-arithmetic}/issue-8460.rs (100%) rename src/test/ui/{pattern/or-pattern-macro-pat.rs => or-patterns/macro-pat.rs} (100%) rename src/test/{compile-fail => ui/panic-handler}/auxiliary/weak-lang-items.rs (100%) rename src/test/{compile-fail => ui/panic-handler}/panic-handler-missing.rs (83%) rename src/test/{compile-fail => ui/panic-handler}/panic-handler-twice.rs (90%) rename src/test/{compile-fail => ui/panic-handler}/weak-lang-item.rs (100%) create mode 100644 src/test/ui/panic-handler/weak-lang-item.stderr rename src/test/{compile-fail => ui/panic-runtime}/auxiliary/depends.rs (100%) rename src/test/{compile-fail => ui/panic-runtime}/auxiliary/needs-panic-runtime.rs (100%) rename src/test/{compile-fail => ui/panic-runtime}/runtime-depend-on-needs-runtime.rs (84%) rename src/test/{compile-fail => ui/panic-runtime}/two-panic-runtimes.rs (90%) rename src/test/{compile-fail => ui/panic-runtime}/unwind-tables-panic-required.rs (91%) rename src/test/{compile-fail => ui/panic-runtime}/unwind-tables-target-required.rs (100%) rename src/test/{compile-fail => ui/panic-runtime}/want-abort-got-unwind.rs (81%) rename src/test/{compile-fail => ui/panic-runtime}/want-abort-got-unwind2.rs (84%) create mode 100644 src/test/ui/panics/panic-2021.rs create mode 100644 src/test/ui/panics/panic-2021.stderr rename src/test/ui/{issues => parser}/auxiliary/issue-21146-inc.rs (100%) rename src/test/ui/{ => parser}/can-begin-expr-check.rs (100%) rename src/test/ui/{ => parser}/can-begin-expr-check.stderr (100%) rename src/test/ui/{issues => parser}/issue-20616-1.rs (100%) rename src/test/ui/{issues => parser}/issue-20616-1.stderr (100%) rename src/test/ui/{issues => parser}/issue-20616-2.rs (100%) rename src/test/ui/{issues => parser}/issue-20616-2.stderr (100%) rename src/test/ui/{issues => parser}/issue-20616-8.rs (100%) rename src/test/ui/{issues => parser}/issue-20616-8.stderr (100%) rename src/test/ui/{issues => parser}/issue-20616-9.rs (100%) rename src/test/ui/{issues => parser}/issue-20616-9.stderr (100%) rename src/test/ui/{issues => parser}/issue-21146.rs (100%) rename src/test/ui/{issues => parser}/issue-21146.stderr (100%) rename src/test/ui/{issues => parser}/issue-34222-1.rs (100%) rename src/test/ui/{issues => parser}/issue-34222-1.stderr (100%) rename src/test/ui/{issues => parser}/issue-43196.rs (100%) rename src/test/ui/{issues => parser}/issue-43196.stderr (100%) rename src/test/ui/{issues => parser}/issue-44021.rs (100%) rename src/test/ui/{issues => parser}/issue-44021.stderr (100%) rename src/test/ui/{issues => parser}/issue-45296.rs (100%) rename src/test/ui/{issues => parser}/issue-45296.stderr (100%) rename src/test/ui/{issues => parser}/issue-46186.fixed (100%) rename src/test/ui/{issues => parser}/issue-46186.rs (100%) rename src/test/ui/{issues => parser}/issue-46186.stderr (100%) rename src/test/ui/{issues => parser}/issue-51602.rs (100%) rename src/test/ui/{issues => parser}/issue-51602.stderr (100%) rename src/test/ui/{issues => parser}/issue-52496.rs (100%) rename src/test/ui/{issues => parser}/issue-52496.stderr (100%) rename src/test/ui/{issues => parser}/issue-57198.rs (100%) rename src/test/ui/{issues => parser}/issue-57198.stderr (100%) rename src/test/ui/{issues => parser}/issue-57684.fixed (100%) rename src/test/ui/{issues => parser}/issue-57684.rs (100%) rename src/test/ui/{issues => parser}/issue-57684.stderr (100%) rename src/test/ui/{issues => parser}/issue-57819.fixed (100%) rename src/test/ui/{issues => parser}/issue-57819.rs (100%) rename src/test/ui/{issues => parser}/issue-57819.stderr (100%) rename src/test/ui/{issues => parser}/issue-58856-1.rs (100%) rename src/test/ui/{issues => parser}/issue-58856-1.stderr (100%) rename src/test/ui/{issues => parser}/issue-58856-2.rs (100%) rename src/test/ui/{issues => parser}/issue-58856-2.stderr (100%) rename src/test/ui/{issues => parser}/issue-60075.rs (100%) rename src/test/ui/{issues => parser}/issue-60075.stderr (100%) rename src/test/ui/{issues => parser}/issue-62554.rs (100%) rename src/test/ui/{issues => parser}/issue-62554.stderr (100%) rename src/test/ui/{issues => parser}/issue-64732.rs (100%) rename src/test/ui/{issues => parser}/issue-64732.stderr (85%) rename src/test/ui/{issues => parser}/issue-66473.rs (100%) rename src/test/ui/{issues => parser}/issue-66473.stderr (100%) rename src/test/ui/{issues => parser}/issue-72253.rs (100%) rename src/test/ui/{issues => parser}/issue-72253.stderr (100%) rename src/test/ui/{issues => parser}/issue-72373.rs (100%) rename src/test/ui/{issues => parser}/issue-72373.stderr (100%) rename src/test/ui/{issues => parser}/issue-75599.rs (100%) rename src/test/ui/{ => parser}/issue-76597.fixed (100%) rename src/test/ui/{ => parser}/issue-76597.rs (100%) rename src/test/ui/{ => parser}/issue-76597.stderr (100%) rename src/test/ui/{issues => parser}/issue-7970b.rs (100%) rename src/test/ui/{issues => parser}/issue-7970b.stderr (100%) create mode 100644 src/test/ui/parser/issue-81806.rs create mode 100644 src/test/ui/parser/issue-81806.stderr rename src/test/ui/{ => parser}/lifetime_starts_expressions.rs (100%) rename src/test/ui/{ => parser}/lifetime_starts_expressions.stderr (100%) create mode 100644 src/test/ui/parser/missing-closing-angle-bracket-eq-constraint.rs create mode 100644 src/test/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr create mode 100644 src/test/ui/parser/multibyte-char-use-seperator-issue-80134.rs create mode 100644 src/test/ui/parser/multibyte-char-use-seperator-issue-80134.stderr create mode 100644 src/test/ui/parser/nested-missing-closing-angle-bracket.rs create mode 100644 src/test/ui/parser/nested-missing-closing-angle-bracket.stderr rename src/test/ui/{ => parser}/parse-assoc-type-lt.rs (100%) rename src/test/ui/{ => parser}/parse-error-correct.rs (100%) rename src/test/ui/{ => parser}/parse-error-correct.stderr (100%) rename src/test/ui/{ => parser}/parse-panic.rs (100%) rename src/test/ui/{ => parser}/parser-recovery-1.rs (100%) rename src/test/ui/{ => parser}/parser-recovery-1.stderr (100%) rename src/test/ui/{ => parser}/parser-recovery-2.rs (100%) rename src/test/ui/{ => parser}/parser-recovery-2.stderr (100%) rename src/test/ui/{ => parser}/parser-unicode-whitespace.rs (100%) rename src/test/ui/{issues => pattern}/issue-12582.rs (100%) rename src/test/ui/{issues => pattern}/issue-14221.rs (100%) rename src/test/ui/{issues => pattern}/issue-14221.stderr (100%) rename src/test/ui/{issues => pattern}/issue-22546.rs (100%) rename src/test/ui/{issues => pattern}/issue-6449.rs (100%) rename src/test/ui/{issues => pattern}/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs (100%) rename src/test/ui/{issues => pattern}/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr (81%) create mode 100644 src/test/ui/pattern/pat-tuple-underfield.rs create mode 100644 src/test/ui/pattern/pat-tuple-underfield.stderr rename src/test/ui/{ => pattern}/size-and-align.rs (100%) rename src/test/ui/pattern/usefulness/{match-empty-exhaustive_patterns.stderr => empty-match.exhaustive_patterns.stderr} (56%) rename src/test/ui/pattern/usefulness/{match-empty.stderr => empty-match.normal.stderr} (56%) create mode 100644 src/test/ui/pattern/usefulness/empty-match.rs delete mode 100644 src/test/ui/pattern/usefulness/integer-ranges/pointer-sized-int-allow.rs rename src/test/ui/pattern/usefulness/integer-ranges/{pointer-sized-int-allow.stderr => pointer-sized-int.allow.stderr} (89%) rename src/test/ui/pattern/usefulness/integer-ranges/{pointer-sized-int-deny.stderr => pointer-sized-int.deny.stderr} (92%) rename src/test/ui/pattern/usefulness/integer-ranges/{pointer-sized-int-deny.rs => pointer-sized-int.rs} (60%) create mode 100644 src/test/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs create mode 100644 src/test/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr delete mode 100644 src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs delete mode 100644 src/test/ui/pattern/usefulness/match-empty.rs create mode 100644 src/test/ui/pattern/usefulness/uninhabited.rs rename src/test/ui/{issues => privacy}/issue-29161.rs (100%) rename src/test/ui/{issues => privacy}/issue-29161.stderr (100%) rename src/test/ui/{issues => privacy}/issue-30079.rs (100%) rename src/test/ui/{issues => privacy}/issue-30079.stderr (100%) rename src/test/{compile-fail => ui/privacy}/issue-46209-private-enum-variant-reexport.rs (100%) create mode 100644 src/test/ui/privacy/issue-46209-private-enum-variant-reexport.stderr rename src/test/ui/{ => privacy}/priv-in-bad-locations.rs (82%) rename src/test/ui/{ => privacy}/priv-in-bad-locations.stderr (97%) create mode 100644 src/test/ui/proc-macro/auxiliary/nonterminal-recollect-attr.rs create mode 100644 src/test/ui/proc-macro/issue-80760-empty-stmt.rs create mode 100644 src/test/ui/proc-macro/issue-80760-empty-stmt.stdout create mode 100644 src/test/ui/proc-macro/issue-81007-item-attrs.rs create mode 100644 src/test/ui/proc-macro/issue-81007-item-attrs.stdout create mode 100644 src/test/ui/proc-macro/issue-81543-item-parse-err.rs create mode 100644 src/test/ui/proc-macro/issue-81543-item-parse-err.stderr create mode 100644 src/test/ui/proc-macro/nonterminal-recollect-attr.rs rename src/test/ui/{ => reachable}/unreachable-code-ret.rs (100%) rename src/test/ui/{ => reachable}/unreachable-code-ret.stderr (100%) rename src/test/ui/{issues => regions}/issue-12470.rs (100%) rename src/test/ui/{issues => regions}/issue-12470.stderr (100%) rename src/test/ui/{issues => regions}/issue-24085.rs (100%) rename src/test/ui/{issues => regions}/issue-2718.rs (100%) rename src/test/ui/{issues => regions}/issue-28848.nll.stderr (100%) rename src/test/ui/{issues => regions}/issue-28848.rs (100%) rename src/test/ui/{issues => regions}/issue-28848.stderr (100%) rename src/test/ui/{issues => regions}/issue-5243.rs (100%) create mode 100644 src/test/ui/resolve/crate-called-as-function.rs create mode 100644 src/test/ui/resolve/crate-called-as-function.stderr rename src/test/ui/{issues => resolve}/issue-10200.rs (100%) rename src/test/ui/{issues => resolve}/issue-10200.stderr (100%) rename src/test/ui/{issues => resolve}/issue-50599.rs (100%) rename src/test/ui/{issues => resolve}/issue-50599.stderr (100%) create mode 100644 src/test/ui/resolve/issue-82156.rs create mode 100644 src/test/ui/resolve/issue-82156.stderr create mode 100644 src/test/ui/resolve/missing-in-namespace.rs create mode 100644 src/test/ui/resolve/missing-in-namespace.stderr rename src/test/ui/{ => rmeta}/auxiliary/rmeta-meta.rs (100%) rename src/test/ui/{ => rmeta}/auxiliary/rmeta-rlib-rpass.rs (100%) rename src/test/ui/{ => rmeta}/auxiliary/rmeta-rlib.rs (100%) rename src/test/ui/{ => rmeta}/auxiliary/rmeta-rmeta.rs (100%) rename src/test/ui/{ => rmeta}/rmeta-lib-pass.rs (100%) rename src/test/ui/{ => rmeta}/rmeta-pass.rs (100%) rename src/test/ui/{ => rmeta}/rmeta-priv-warn.rs (100%) rename src/test/ui/{ => rmeta}/rmeta-rpass.rs (100%) rename src/test/ui/{ => rmeta}/rmeta.rs (100%) rename src/test/ui/{ => rmeta}/rmeta.stderr (100%) rename src/test/ui/{ => rmeta}/rmeta_lib.rs (100%) rename src/test/ui/{ => rmeta}/rmeta_lib.stderr (100%) rename src/test/ui/{ => rmeta}/rmeta_meta_main.rs (100%) rename src/test/ui/{ => rmeta}/rmeta_meta_main.stderr (100%) rename src/test/ui/{issues => simd}/issue-32947.rs (100%) delete mode 100644 src/test/ui/similar-tokens.fixed rename src/test/{compile-fail/specialization/issue-50452.rs => ui/specialization/issue-50452-fail.rs} (95%) create mode 100644 src/test/ui/specialization/issue-50452-fail.stderr create mode 100644 src/test/ui/suggestions/field-access.fixed create mode 100644 src/test/ui/suggestions/field-access.rs create mode 100644 src/test/ui/suggestions/field-access.stderr create mode 100644 src/test/ui/suggestions/issue-81098.rs create mode 100644 src/test/ui/suggestions/issue-81098.stderr create mode 100644 src/test/ui/suggestions/non-existent-field-present-in-subfield-recursion-limit.rs create mode 100644 src/test/ui/suggestions/non-existent-field-present-in-subfield-recursion-limit.stderr create mode 100644 src/test/ui/suggestions/non-existent-field-present-in-subfield.fixed create mode 100644 src/test/ui/suggestions/non-existent-field-present-in-subfield.rs create mode 100644 src/test/ui/suggestions/non-existent-field-present-in-subfield.stderr delete mode 100644 src/test/ui/suggestions/vec-macro-in-pattern.fixed delete mode 100644 src/test/ui/suggestions/vec-macro-in-pattern.rs delete mode 100644 src/test/ui/suggestions/vec-macro-in-pattern.stderr create mode 100644 src/test/ui/terminal-width/tabs-trimming.rs create mode 100644 src/test/ui/terminal-width/tabs-trimming.stderr rename src/test/ui/{issues => test-attrs}/issue-53675-a-test-called-panic.rs (100%) rename src/test/ui/{ => threads-sendsync}/eprint-on-tls-drop.rs (100%) rename src/test/{compile-fail => ui/threads-sendsync}/issue-43733-2.rs (96%) rename src/test/ui/{issues => threads-sendsync}/issue-43733.rs (100%) rename src/test/ui/{issues => threads-sendsync}/issue-43733.stderr (100%) rename src/test/ui/{issues => threads-sendsync}/issue-4446.rs (100%) rename src/test/ui/{issues => traits}/issue-33140-hack-boundaries.rs (100%) rename src/test/ui/{issues => traits}/issue-33140-hack-boundaries.stderr (100%) rename src/test/ui/{issues => traits}/issue-3683.rs (100%) rename src/test/ui/{issues => traits}/issue-56202.rs (100%) rename src/test/ui/{issues => traits}/issue-56488.rs (100%) rename src/test/ui/{issues => traits}/issue-59029-2.rs (100%) rename src/test/ui/{issues => traits}/issue-6128.rs (100%) rename src/test/ui/{issues => traits}/issue-6334.rs (100%) rename src/test/ui/{issues => traits}/issue-65284-suggest-generic-trait-bound.rs (100%) rename src/test/ui/{issues => traits}/issue-65284-suggest-generic-trait-bound.stderr (100%) rename src/test/ui/{issues => traits}/issue-65673.rs (100%) rename src/test/ui/{issues => traits}/issue-65673.stderr (100%) rename src/test/ui/{issues => traits}/issue-9394-inherited-trait-calls.rs (100%) create mode 100644 src/test/ui/traits/mutual-recursion-issue-75860.rs create mode 100644 src/test/ui/traits/mutual-recursion-issue-75860.stderr create mode 100644 src/test/ui/typeck/issue-81293.rs create mode 100644 src/test/ui/typeck/issue-81293.stderr delete mode 100644 src/test/ui/typestate-cfg-nesting.rs delete mode 100644 src/test/ui/underscore-imports/hygiene.stderr rename src/test/ui/{issues => unsafe}/issue-45087-unreachable-unsafe.rs (100%) rename src/test/ui/{issues => unsafe}/issue-45087-unreachable-unsafe.stderr (100%) delete mode 100644 src/test/ui/unsafe/unsafe-move-val-init.rs delete mode 100644 src/test/ui/unsafe/unsafe-move-val-init.stderr create mode 100644 src/test/ui/unsized/unchanged-param.rs delete mode 100644 src/test/ui/unsupported-cast.rs create mode 100644 src/test/ui/wasm/wasm-hang-issue-76281.rs create mode 100644 src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs create mode 100644 src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr create mode 100644 src/tools/jsondocck/Cargo.toml create mode 100644 src/tools/jsondocck/src/cache.rs create mode 100644 src/tools/jsondocck/src/config.rs create mode 100644 src/tools/jsondocck/src/error.rs create mode 100644 src/tools/jsondocck/src/main.rs create mode 100644 src/tools/rust-installer/.github/workflows/ci.yml delete mode 100644 src/tools/rust-installer/.travis.yml create mode 100644 src/tools/rust-installer/src/compression.rs delete mode 100644 src/tools/tidy/src/cargo.rs create mode 100644 vendor/anyhow/src/ptr.rs create mode 100644 vendor/anyhow/tests/test_ffi.rs create mode 100644 vendor/anyhow/tests/ui/temporary-value.rs create mode 100644 vendor/anyhow/tests/ui/temporary-value.stderr create mode 100644 vendor/arrayvec/CHANGELOG.md create mode 100644 vendor/arrayvec/README.md delete mode 100644 vendor/arrayvec/README.rst create mode 100644 vendor/arrayvec/ci/miri.sh delete mode 100644 vendor/byteorder/build.rs create mode 100644 vendor/byteorder/rustfmt.toml rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/.cargo-checksum.json (55%) rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/Cargo.toml (96%) rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/LICENSE-MIT (100%) rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/README.md (100%) rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/src/dependency.rs (100%) rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/src/diagnostic.rs (100%) rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/src/errors.rs (100%) rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/src/lib.rs (98%) rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/src/messages.rs (100%) rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/tests/selftest.rs (100%) rename vendor/{cargo_metadata-0.11.2 => cargo_metadata-0.11.1}/tests/test_samples.rs (98%) create mode 100644 vendor/chalk-ir/src/fold/in_place.rs create mode 100644 vendor/chalk-solve/src/clauses/builtin_traits/discriminant_kind.rs delete mode 100644 vendor/chalk-solve/src/recursive/lib.rs create mode 100644 vendor/chrono/src/sys.rs create mode 100644 vendor/chrono/src/sys/stub.rs create mode 100644 vendor/chrono/src/sys/unix.rs create mode 100644 vendor/chrono/src/sys/windows.rs delete mode 100644 vendor/cloudabi/.cargo-checksum.json delete mode 100644 vendor/cloudabi/cloudabi.rs create mode 100644 vendor/compiler_builtins/src/mem/impls.rs rename vendor/compiler_builtins/src/{mem.rs => mem/mod.rs} (90%) create mode 100644 vendor/compiler_builtins/src/mem/x86_64.rs create mode 100644 vendor/const_fn/.cargo-checksum.json create mode 100644 vendor/const_fn/CHANGELOG.md create mode 100644 vendor/const_fn/Cargo.toml create mode 100644 vendor/const_fn/LICENSE-APACHE create mode 100644 vendor/const_fn/LICENSE-MIT create mode 100644 vendor/const_fn/README.md create mode 100644 vendor/const_fn/build.rs create mode 100644 vendor/const_fn/src/ast.rs create mode 100644 vendor/const_fn/src/error.rs create mode 100644 vendor/const_fn/src/iter.rs create mode 100644 vendor/const_fn/src/lib.rs create mode 100644 vendor/const_fn/src/to_tokens.rs create mode 100644 vendor/const_fn/src/utils.rs rename vendor/crossbeam-channel/src/flavors/{after.rs => at.rs} (79%) create mode 100644 vendor/crossbeam-deque-0.7.3/.cargo-checksum.json create mode 100644 vendor/crossbeam-deque-0.7.3/CHANGELOG.md create mode 100644 vendor/crossbeam-deque-0.7.3/Cargo.toml rename vendor/{itertools => crossbeam-deque-0.7.3}/LICENSE-APACHE (100%) create mode 100644 vendor/crossbeam-deque-0.7.3/LICENSE-MIT create mode 100644 vendor/crossbeam-deque-0.7.3/README.md create mode 100644 vendor/crossbeam-deque-0.7.3/src/lib.rs create mode 100644 vendor/crossbeam-deque-0.7.3/tests/fifo.rs create mode 100644 vendor/crossbeam-deque-0.7.3/tests/injector.rs create mode 100644 vendor/crossbeam-deque-0.7.3/tests/lifo.rs create mode 100644 vendor/crossbeam-deque-0.7.3/tests/steal.rs create mode 100644 vendor/crossbeam-deque/src/deque.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/.cargo-checksum.json create mode 100644 vendor/crossbeam-epoch-0.8.2/CHANGELOG.md create mode 100644 vendor/crossbeam-epoch-0.8.2/Cargo.lock create mode 100644 vendor/crossbeam-epoch-0.8.2/Cargo.toml rename vendor/{object => crossbeam-epoch-0.8.2}/LICENSE-APACHE (100%) create mode 100644 vendor/crossbeam-epoch-0.8.2/LICENSE-MIT create mode 100644 vendor/crossbeam-epoch-0.8.2/README.md create mode 100644 vendor/crossbeam-epoch-0.8.2/benches/defer.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/benches/flush.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/benches/pin.rs rename vendor/{crossbeam-epoch => crossbeam-epoch-0.8.2}/build.rs (100%) create mode 100644 vendor/crossbeam-epoch-0.8.2/examples/sanitize.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/examples/treiber_stack.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/atomic.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/collector.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/default.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/deferred.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/epoch.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/guard.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/internal.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/lib.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/sync/list.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/sync/mod.rs create mode 100644 vendor/crossbeam-epoch-0.8.2/src/sync/queue.rs create mode 100644 vendor/crossbeam-utils/.cargo-checksum.json create mode 100644 vendor/crossbeam-utils/CHANGELOG.md create mode 100644 vendor/crossbeam-utils/Cargo.toml create mode 100644 vendor/crossbeam-utils/LICENSE-APACHE create mode 100644 vendor/crossbeam-utils/LICENSE-MIT create mode 100644 vendor/crossbeam-utils/README.md create mode 100644 vendor/crossbeam-utils/benches/atomic_cell.rs create mode 100644 vendor/crossbeam-utils/build.rs create mode 100644 vendor/crossbeam-utils/src/atomic/atomic_cell.rs create mode 100644 vendor/crossbeam-utils/src/atomic/consume.rs create mode 100644 vendor/crossbeam-utils/src/atomic/mod.rs create mode 100644 vendor/crossbeam-utils/src/atomic/seq_lock.rs create mode 100644 vendor/crossbeam-utils/src/atomic/seq_lock_wide.rs create mode 100644 vendor/crossbeam-utils/src/backoff.rs create mode 100644 vendor/crossbeam-utils/src/cache_padded.rs create mode 100644 vendor/crossbeam-utils/src/lib.rs create mode 100644 vendor/crossbeam-utils/src/sync/mod.rs create mode 100644 vendor/crossbeam-utils/src/sync/parker.rs create mode 100644 vendor/crossbeam-utils/src/sync/sharded_lock.rs create mode 100644 vendor/crossbeam-utils/src/sync/wait_group.rs create mode 100644 vendor/crossbeam-utils/src/thread.rs create mode 100644 vendor/crossbeam-utils/tests/atomic_cell.rs create mode 100644 vendor/crossbeam-utils/tests/cache_padded.rs create mode 100644 vendor/crossbeam-utils/tests/parker.rs create mode 100644 vendor/crossbeam-utils/tests/sharded_lock.rs create mode 100644 vendor/crossbeam-utils/tests/thread.rs create mode 100644 vendor/crossbeam-utils/tests/wait_group.rs create mode 100644 vendor/dissimilar/.cargo-checksum.json create mode 100644 vendor/dissimilar/Cargo.toml create mode 100644 vendor/dissimilar/LICENSE-APACHE create mode 100644 vendor/dissimilar/LICENSE-MIT create mode 100644 vendor/dissimilar/README.md create mode 100644 vendor/dissimilar/benches/bench.rs create mode 100644 vendor/dissimilar/benches/document1.txt create mode 100644 vendor/dissimilar/benches/document2.txt create mode 100644 vendor/dissimilar/src/find.rs create mode 100644 vendor/dissimilar/src/lib.rs create mode 100644 vendor/dissimilar/src/range.rs create mode 100644 vendor/dissimilar/src/tests.rs create mode 100644 vendor/dissimilar/tests/test.rs create mode 100644 vendor/form_urlencoded/.cargo-checksum.json create mode 100644 vendor/form_urlencoded/Cargo.toml create mode 100644 vendor/form_urlencoded/LICENSE-APACHE create mode 100644 vendor/form_urlencoded/LICENSE-MIT rename vendor/{url/src/form_urlencoded.rs => form_urlencoded/src/lib.rs} (90%) rename vendor/{url => form_urlencoded}/src/query_encoding.rs (100%) create mode 100644 vendor/heck/src/shouty_kebab.rs create mode 100644 vendor/indexmap/src/serde_seq.rs rename vendor/{itertools => itertools-0.9.0}/.cargo-checksum.json (100%) rename vendor/{itertools => itertools-0.9.0}/CHANGELOG.md (100%) rename vendor/{itertools => itertools-0.9.0}/Cargo.lock (100%) rename vendor/{itertools => itertools-0.9.0}/Cargo.toml (100%) create mode 100644 vendor/itertools-0.9.0/LICENSE-APACHE rename vendor/{itertools => itertools-0.9.0}/LICENSE-MIT (100%) rename vendor/{itertools => itertools-0.9.0}/README.rst (100%) rename vendor/{itertools => itertools-0.9.0}/benches/bench1.rs (100%) rename vendor/{itertools => itertools-0.9.0}/benches/combinations_with_replacement.rs (100%) rename vendor/{itertools => itertools-0.9.0}/benches/extra/mod.rs (100%) rename vendor/{itertools => itertools-0.9.0}/benches/extra/zipslices.rs (100%) rename vendor/{itertools => itertools-0.9.0}/benches/fold_specialization.rs (100%) rename vendor/{itertools => itertools-0.9.0}/benches/tree_fold1.rs (100%) rename vendor/{itertools => itertools-0.9.0}/benches/tuple_combinations.rs (100%) rename vendor/{itertools => itertools-0.9.0}/benches/tuples.rs (100%) rename vendor/{itertools => itertools-0.9.0}/examples/iris.data (100%) rename vendor/{itertools => itertools-0.9.0}/examples/iris.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/adaptors/mod.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/adaptors/multi_product.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/combinations.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/combinations_with_replacement.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/concat_impl.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/cons_tuples_impl.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/diff.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/either_or_both.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/exactly_one_err.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/format.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/free.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/group_map.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/groupbylazy.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/impl_macros.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/intersperse.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/kmerge_impl.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/lazy_buffer.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/lib.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/merge_join.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/minmax.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/multipeek_impl.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/pad_tail.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/peeking_take_while.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/permutations.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/process_results_impl.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/put_back_n_impl.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/rciter_impl.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/repeatn.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/size_hint.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/sources.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/tee.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/tuple_impl.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/unique_impl.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/with_position.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/zip_eq_impl.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/zip_longest.rs (100%) rename vendor/{itertools => itertools-0.9.0}/src/ziptuple.rs (100%) rename vendor/{itertools => itertools-0.9.0}/tests/adaptors_no_collect.rs (100%) rename vendor/{itertools => itertools-0.9.0}/tests/fold_specialization.rs (100%) rename vendor/{itertools => itertools-0.9.0}/tests/merge_join.rs (100%) rename vendor/{itertools => itertools-0.9.0}/tests/peeking_take_while.rs (100%) rename vendor/{itertools => itertools-0.9.0}/tests/quick.rs (100%) rename vendor/{itertools => itertools-0.9.0}/tests/specializations.rs (100%) rename vendor/{itertools => itertools-0.9.0}/tests/test_core.rs (100%) rename vendor/{itertools => itertools-0.9.0}/tests/test_std.rs (100%) rename vendor/{itertools => itertools-0.9.0}/tests/tuples.rs (100%) rename vendor/{itertools => itertools-0.9.0}/tests/zip.rs (100%) create mode 100644 vendor/libc/src/unix/bsd/apple/b64/aarch64/align.rs create mode 100644 vendor/libc/src/unix/bsd/apple/b64/aarch64/mod.rs create mode 100644 vendor/libc/src/unix/bsd/apple/b64/x86_64/align.rs create mode 100644 vendor/libc/src/unix/bsd/apple/b64/x86_64/mod.rs create mode 100644 vendor/libc/src/unix/bsd/freebsdlike/freebsd/freebsd13/b64.rs create mode 100644 vendor/libc/src/unix/bsd/freebsdlike/freebsd/freebsd13/mod.rs create mode 100644 vendor/libc/src/unix/linux_like/linux/gnu/b64/aarch64/ilp32.rs create mode 100644 vendor/libc/src/unix/linux_like/linux/gnu/b64/aarch64/lp64.rs mode change 100644 => 100755 vendor/libc/src/vxworks/mod.rs delete mode 100644 vendor/libc/triagebot.toml create mode 100644 vendor/log/benches/value.rs create mode 100644 vendor/log/src/kv/value.rs delete mode 100644 vendor/log/src/kv/value/fill.rs delete mode 100644 vendor/log/src/kv/value/impls.rs delete mode 100644 vendor/log/src/kv/value/internal/cast.rs delete mode 100644 vendor/log/src/kv/value/internal/fmt.rs delete mode 100644 vendor/log/src/kv/value/internal/mod.rs delete mode 100644 vendor/log/src/kv/value/internal/sval.rs delete mode 100644 vendor/log/src/kv/value/mod.rs delete mode 100644 vendor/log/src/kv/value/test.rs delete mode 100644 vendor/log/tests/filters.rs delete mode 100644 vendor/log/tests/macros.rs create mode 100644 vendor/memchr/src/tests/x86_64-soft_float.json create mode 100644 vendor/memoffset-0.5.5/.cargo-checksum.json create mode 100644 vendor/memoffset-0.5.5/Cargo.toml create mode 100644 vendor/memoffset-0.5.5/LICENSE create mode 100644 vendor/memoffset-0.5.5/README.md create mode 100644 vendor/memoffset-0.5.5/build.rs create mode 100644 vendor/memoffset-0.5.5/ci/miri.sh create mode 100644 vendor/memoffset-0.5.5/src/lib.rs create mode 100644 vendor/memoffset-0.5.5/src/offset_of.rs create mode 100644 vendor/memoffset-0.5.5/src/raw_field.rs create mode 100644 vendor/memoffset-0.5.5/src/span_of.rs create mode 100644 vendor/miniz_oxide/LICENSE-APACHE.md create mode 100644 vendor/miniz_oxide/LICENSE-MIT.md create mode 100644 vendor/miniz_oxide/LICENSE-ZLIB.md create mode 100755 vendor/miniz_oxide/build.rs create mode 100644 vendor/num-traits/src/ops/overflowing.rs rename vendor/{object => object-0.22.0}/.cargo-checksum.json (100%) rename vendor/{object => object-0.22.0}/Cargo.lock (100%) rename vendor/{object => object-0.22.0}/Cargo.toml (100%) create mode 100644 vendor/object-0.22.0/LICENSE-APACHE rename vendor/{object => object-0.22.0}/LICENSE-MIT (100%) rename vendor/{object => object-0.22.0}/README.md (100%) rename vendor/{object => object-0.22.0}/examples/ar.rs (100%) rename vendor/{object => object-0.22.0}/examples/nm.rs (100%) rename vendor/{object => object-0.22.0}/examples/objcopy.rs (100%) rename vendor/{object => object-0.22.0}/examples/objdump.rs (100%) rename vendor/{object => object-0.22.0}/examples/objectmap.rs (100%) rename vendor/{object => object-0.22.0}/src/archive.rs (100%) rename vendor/{object => object-0.22.0}/src/common.rs (100%) rename vendor/{object => object-0.22.0}/src/elf.rs (100%) rename vendor/{object => object-0.22.0}/src/endian.rs (100%) rename vendor/{object => object-0.22.0}/src/lib.rs (100%) rename vendor/{object => object-0.22.0}/src/macho.rs (100%) rename vendor/{object => object-0.22.0}/src/pe.rs (100%) rename vendor/{object => object-0.22.0}/src/pod.rs (100%) rename vendor/{object => object-0.22.0}/src/read/any.rs (100%) rename vendor/{object => object-0.22.0}/src/read/archive.rs (100%) rename vendor/{object => object-0.22.0}/src/read/coff/comdat.rs (100%) rename vendor/{object => object-0.22.0}/src/read/coff/file.rs (100%) rename vendor/{object => object-0.22.0}/src/read/coff/mod.rs (100%) rename vendor/{object => object-0.22.0}/src/read/coff/relocation.rs (100%) rename vendor/{object => object-0.22.0}/src/read/coff/section.rs (100%) rename vendor/{object => object-0.22.0}/src/read/coff/symbol.rs (100%) rename vendor/{object => object-0.22.0}/src/read/elf/comdat.rs (100%) rename vendor/{object => object-0.22.0}/src/read/elf/compression.rs (100%) rename vendor/{object => object-0.22.0}/src/read/elf/file.rs (100%) rename vendor/{object => object-0.22.0}/src/read/elf/mod.rs (100%) rename vendor/{object => object-0.22.0}/src/read/elf/note.rs (100%) rename vendor/{object => object-0.22.0}/src/read/elf/relocation.rs (100%) rename vendor/{object => object-0.22.0}/src/read/elf/section.rs (100%) rename vendor/{object => object-0.22.0}/src/read/elf/segment.rs (100%) rename vendor/{object => object-0.22.0}/src/read/elf/symbol.rs (100%) rename vendor/{object => object-0.22.0}/src/read/macho/file.rs (100%) rename vendor/{object => object-0.22.0}/src/read/macho/load_command.rs (100%) rename vendor/{object => object-0.22.0}/src/read/macho/mod.rs (100%) rename vendor/{object => object-0.22.0}/src/read/macho/relocation.rs (100%) rename vendor/{object => object-0.22.0}/src/read/macho/section.rs (100%) rename vendor/{object => object-0.22.0}/src/read/macho/segment.rs (100%) rename vendor/{object => object-0.22.0}/src/read/macho/symbol.rs (100%) rename vendor/{object => object-0.22.0}/src/read/mod.rs (100%) rename vendor/{object => object-0.22.0}/src/read/pe/file.rs (100%) rename vendor/{object => object-0.22.0}/src/read/pe/mod.rs (100%) rename vendor/{object => object-0.22.0}/src/read/pe/section.rs (100%) rename vendor/{object => object-0.22.0}/src/read/traits.rs (100%) rename vendor/{object => object-0.22.0}/src/read/util.rs (100%) rename vendor/{object => object-0.22.0}/src/read/wasm.rs (100%) rename vendor/{object => object-0.22.0}/src/write/coff.rs (100%) rename vendor/{object => object-0.22.0}/src/write/elf.rs (100%) rename vendor/{object => object-0.22.0}/src/write/macho.rs (100%) rename vendor/{object => object-0.22.0}/src/write/mod.rs (100%) rename vendor/{object => object-0.22.0}/src/write/string.rs (100%) rename vendor/{object => object-0.22.0}/src/write/util.rs (100%) rename vendor/{object => object-0.22.0}/tests/integration.rs (100%) rename vendor/{object => object-0.22.0}/tests/parse_self.rs (100%) rename vendor/{object => object-0.22.0}/tests/round_trip/bss.rs (100%) rename vendor/{object => object-0.22.0}/tests/round_trip/comdat.rs (100%) rename vendor/{object => object-0.22.0}/tests/round_trip/common.rs (100%) rename vendor/{object => object-0.22.0}/tests/round_trip/elf.rs (100%) rename vendor/{object => object-0.22.0}/tests/round_trip/mod.rs (100%) rename vendor/{object => object-0.22.0}/tests/round_trip/tls.rs (100%) create mode 100644 vendor/once_cell/bors.toml create mode 100644 vendor/once_cell/src/race.rs rename vendor/once_cell/tests/{test.rs => it.rs} (66%) delete mode 100644 vendor/parking_lot_core/src/thread_parker/cloudabi.rs create mode 100644 vendor/pin-project-lite/.cargo-checksum.json create mode 100644 vendor/pin-project-lite/CHANGELOG.md create mode 100644 vendor/pin-project-lite/Cargo.toml create mode 100644 vendor/pin-project-lite/LICENSE-APACHE create mode 100644 vendor/pin-project-lite/LICENSE-MIT create mode 100644 vendor/pin-project-lite/README.md create mode 100644 vendor/pin-project-lite/src/lib.rs create mode 100644 vendor/pin-project-lite/tests/auxiliary/mod.rs create mode 100644 vendor/pin-project-lite/tests/compiletest.rs create mode 100644 vendor/pin-project-lite/tests/drop_order.rs create mode 100644 vendor/pin-project-lite/tests/include/basic.rs create mode 100644 vendor/pin-project-lite/tests/lint.rs create mode 100644 vendor/pin-project-lite/tests/proper_unpin.rs create mode 100644 vendor/pin-project-lite/tests/test.rs create mode 100644 vendor/pin-project-lite/tests/ui/conflict-drop.rs create mode 100644 vendor/pin-project-lite/tests/ui/conflict-drop.stderr create mode 100644 vendor/pin-project-lite/tests/ui/conflict-unpin.rs create mode 100644 vendor/pin-project-lite/tests/ui/conflict-unpin.stderr create mode 100644 vendor/pin-project-lite/tests/ui/invalid-bounds.rs create mode 100644 vendor/pin-project-lite/tests/ui/invalid-bounds.stderr create mode 100644 vendor/pin-project-lite/tests/ui/invalid.rs create mode 100644 vendor/pin-project-lite/tests/ui/invalid.stderr create mode 100644 vendor/pin-project-lite/tests/ui/overlapping_lifetime_names.rs create mode 100644 vendor/pin-project-lite/tests/ui/overlapping_lifetime_names.stderr create mode 100644 vendor/pin-project-lite/tests/ui/overlapping_unpin_struct.rs create mode 100644 vendor/pin-project-lite/tests/ui/overlapping_unpin_struct.stderr create mode 100644 vendor/pin-project-lite/tests/ui/packed.rs create mode 100644 vendor/pin-project-lite/tests/ui/packed.stderr create mode 100644 vendor/pin-project-lite/tests/ui/unpin_sneaky.rs create mode 100644 vendor/pin-project-lite/tests/ui/unpin_sneaky.stderr create mode 100644 vendor/pin-project-lite/tests/ui/unsupported.rs create mode 100644 vendor/pin-project-lite/tests/ui/unsupported.stderr create mode 100644 vendor/rayon/src/iter/flat_map_iter.rs create mode 100644 vendor/rayon/src/iter/flatten_iter.rs create mode 100644 vendor/rayon/src/iter/positions.rs create mode 100644 vendor/rayon/src/string.rs create mode 100644 vendor/redox_syscall-0.1.57/.cargo-checksum.json create mode 100644 vendor/redox_syscall-0.1.57/Cargo.toml create mode 100644 vendor/redox_syscall-0.1.57/LICENSE create mode 100644 vendor/redox_syscall-0.1.57/README.md create mode 100644 vendor/redox_syscall-0.1.57/src/arch/aarch64.rs create mode 100644 vendor/redox_syscall-0.1.57/src/arch/arm.rs create mode 100644 vendor/redox_syscall-0.1.57/src/arch/nonredox.rs create mode 100644 vendor/redox_syscall-0.1.57/src/arch/x86.rs create mode 100644 vendor/redox_syscall-0.1.57/src/arch/x86_64.rs create mode 100644 vendor/redox_syscall-0.1.57/src/call.rs create mode 100644 vendor/redox_syscall-0.1.57/src/data.rs create mode 100644 vendor/redox_syscall-0.1.57/src/error.rs create mode 100644 vendor/redox_syscall-0.1.57/src/flag.rs create mode 100644 vendor/redox_syscall-0.1.57/src/io/dma.rs create mode 100644 vendor/redox_syscall-0.1.57/src/io/io.rs create mode 100644 vendor/redox_syscall-0.1.57/src/io/mmio.rs create mode 100644 vendor/redox_syscall-0.1.57/src/io/mod.rs create mode 100644 vendor/redox_syscall-0.1.57/src/io/pio.rs create mode 100644 vendor/redox_syscall-0.1.57/src/lib.rs create mode 100644 vendor/redox_syscall-0.1.57/src/number.rs create mode 100755 vendor/redox_syscall-0.1.57/src/scheme/generate.sh create mode 100644 vendor/redox_syscall-0.1.57/src/scheme/mod.rs create mode 100644 vendor/redox_syscall-0.1.57/src/scheme/scheme.rs create mode 100644 vendor/redox_syscall-0.1.57/src/scheme/scheme_block.rs create mode 100644 vendor/redox_syscall-0.1.57/src/scheme/scheme_block_mut.rs create mode 100644 vendor/redox_syscall-0.1.57/src/scheme/scheme_mut.rs create mode 100644 vendor/redox_syscall-0.1.57/src/tests.rs create mode 100644 vendor/redox_syscall/src/scheme/seek.rs create mode 100644 vendor/regex/tests/regression_fuzz.rs delete mode 100644 vendor/rls-data/src/serde_expanded.rs delete mode 100644 vendor/rls-span/src/serde_expanded.rs delete mode 100644 vendor/serde/src/de/from_primitive.rs create mode 100644 vendor/serde/src/de/seed.rs delete mode 100644 vendor/serde/src/export.rs rename vendor/serde/src/private/{macros.rs => doc.rs} (88%) create mode 100644 vendor/serde/src/private/size_hint.rs create mode 100644 vendor/serde_derive/src/internals/receiver.rs create mode 100644 vendor/serde_derive/src/internals/respan.rs delete mode 100644 vendor/sharded-slab/CONTRIBUTING.md create mode 100644 vendor/sharded-slab/src/pool.rs delete mode 100644 vendor/sharded-slab/src/pool/mod.rs delete mode 100644 vendor/sharded-slab/src/pool/tests.rs create mode 100644 vendor/sharded-slab/src/tests/loom_pool.rs rename vendor/sharded-slab/src/{tests.rs => tests/loom_slab.rs} (71%) create mode 100644 vendor/sharded-slab/src/tests/mod.rs delete mode 100644 vendor/sharded-slab/tests/tests.rs delete mode 100644 vendor/socket2/9f0fbf2.diff create mode 100644 vendor/socket2/SO_ACCEPTCONN.patch delete mode 100644 vendor/socket2/TODO.refactor create mode 100644 vendor/socket2/diff.patch delete mode 100644 vendor/socket2/socket_type.diff create mode 100644 vendor/tinyvec/CHANGELOG.md create mode 100644 vendor/tinyvec/LICENSE-APACHE.md create mode 100644 vendor/tinyvec/LICENSE-MIT.md delete mode 100644 vendor/tinyvec/appveyor.yml create mode 100644 vendor/tinyvec/gen-array-impls.sh create mode 100644 vendor/tinyvec/src-backup/arrayset.rs create mode 100644 vendor/tinyvec/src/array/const_generic_impl.rs create mode 100644 vendor/tinyvec/src/array/generated_impl.rs create mode 100644 vendor/tinyvec/src/arrayvec_drain.rs create mode 100644 vendor/tinyvec/src/slicevec.rs create mode 100644 vendor/tinyvec_macros/.cargo-checksum.json rename vendor/{cloudabi => tinyvec_macros}/Cargo.toml (55%) create mode 100644 vendor/tinyvec_macros/LICENSE create mode 100644 vendor/tinyvec_macros/src/lib.rs create mode 100644 vendor/tracing-subscriber/benches/enter.rs create mode 100644 vendor/tracing-subscriber/src/fmt/format/pretty.rs create mode 100644 vendor/tracing-tree/examples/quiet.rs create mode 100644 vendor/tracing-tree/examples/quiet.stdout create mode 100644 vendor/tracing/src/instrument.rs create mode 100644 vendor/tracing/tests/filters_dont_leak.rs mode change 100755 => 100644 vendor/unicode-normalization/src/no_std_prelude.rs create mode 100644 vendor/unicode-segmentation/benches/graphemes.rs mode change 100755 => 100644 vendor/unicode-segmentation/scripts/unicode.py mode change 100755 => 100644 vendor/unicode-segmentation/scripts/unicode_gen_breaktests.py delete mode 100644 vendor/url/README.md delete mode 100644 vendor/url/UPGRADING.md delete mode 100644 vendor/url/appveyor.yml delete mode 100644 vendor/url/benches/parse_url.rs delete mode 100644 vendor/url/tests/data.rs delete mode 100644 vendor/url/tests/setters_tests.json delete mode 100644 vendor/url/tests/unit.rs delete mode 100644 vendor/url/tests/urltestdata.json diff --git a/Cargo.lock b/Cargo.lock index 89ac858a61..2b68f72581 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,12 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" +[[package]] +name = "array_tool" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271" + [[package]] name = "arrayref" version = "0.3.6" @@ -130,18 +136,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -[[package]] -name = "backtrace" -version = "0.3.55" -dependencies = [ - "addr2line", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base64" version = "0.11.0" @@ -279,13 +273,9 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" -version = "0.4.12" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "bytesize" @@ -295,7 +285,7 @@ checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da" [[package]] name = "cargo" -version = "0.51.0" +version = "0.52.0" dependencies = [ "anyhow", "atty", @@ -421,6 +411,7 @@ dependencies = [ "remove_dir_all", "serde_json", "tar", + "toml", "url 2.1.1", ] @@ -489,9 +480,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.36.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f88ce4deae1dace71e49b7611cfae2d5489de3530d6daba5758043c47ac3a10" +checksum = "3983193cacd81f0f924acb666b7fe5e1a0d81db9f113fa69203eda7ea8ce8b6c" dependencies = [ "proc-macro2", "quote", @@ -501,9 +492,9 @@ dependencies = [ [[package]] name = "chalk-engine" -version = "0.36.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e34c9b1b10616782143d7f49490f91ae94afaf2202de3ab0b2835e78b4f0ccc" +checksum = "05a171ce5abbf0fbd06f221ab80ab182c7ef78603d23b858bc44e7ce8a86a396" dependencies = [ "chalk-derive", "chalk-ir", @@ -514,19 +505,20 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.36.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63362c629c2014ab639b04029070763fb8224df136d1363d30e9ece4c8877da3" +checksum = "a522f53af971e7678f472d687e053120157b3ae26e2ebd5ecbc0f5ab124f2cb6" dependencies = [ + "bitflags", "chalk-derive", "lazy_static", ] [[package]] name = "chalk-solve" -version = "0.36.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac338a67af52a7f50bb2f8232e730a3518ce432dbe303246acfe525ddd838c7" +checksum = "cdf79fb77a567e456a170f7ec84ea6584163d4ba3f13660cd182013d34ca667c" dependencies = [ "chalk-derive", "chalk-ir", @@ -568,7 +560,7 @@ dependencies = [ [[package]] name = "clippy" -version = "0.0.212" +version = "0.1.51" dependencies = [ "cargo_metadata 0.12.0", "clippy-mini-macro-test", @@ -589,7 +581,7 @@ version = "0.2.0" [[package]] name = "clippy_lints" -version = "0.0.212" +version = "0.1.51" dependencies = [ "cargo_metadata 0.12.0", "if_chain", @@ -608,15 +600,6 @@ dependencies = [ "url 2.1.1", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "cloudabi" version = "0.1.0" @@ -666,9 +649,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.36" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369" +checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b" dependencies = [ "cc", "rustc-std-workspace-core", @@ -763,7 +746,7 @@ checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" [[package]] name = "crates-io" -version = "0.31.1" +version = "0.33.0" dependencies = [ "anyhow", "curl", @@ -784,12 +767,12 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" dependencies = [ - "crossbeam-utils 0.7.2", - "maybe-uninit", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.0", ] [[package]] @@ -1118,28 +1101,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "fake-simd" version = "0.1.2" @@ -1255,6 +1216,103 @@ version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" +[[package]] +name = "futures" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" + +[[package]] +name = "futures-executor" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" + +[[package]] +name = "futures-macro" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" + +[[package]] +name = "futures-task" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" +dependencies = [ + "futures 0.1.29", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite 0.2.4", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + [[package]] name = "fwdansi" version = "1.1.0" @@ -1330,9 +1388,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.13.14" +version = "0.13.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186dd99cc77576e58344ad614fa9bb27bad9d048f85de3ca850c1f4e8b048260" +checksum = "1d250f5f82326884bd39c2853577e70a121775db76818ffa452ed1e80de12986" dependencies = [ "bitflags", "libc", @@ -1630,14 +1688,40 @@ version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92c245af8786f6ac35f95ca14feca9119e71339aaab41e878e7cdd655c97e9e5" +[[package]] +name = "jsondocck" +version = "0.1.0" +dependencies = [ + "getopts", + "jsonpath_lib", + "lazy_static", + "regex", + "serde", + "serde_json", + "shlex", +] + +[[package]] +name = "jsonpath_lib" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61352ec23883402b7d30b3313c16cbabefb8907361c4eb669d990cbb87ceee5a" +dependencies = [ + "array_tool", + "env_logger 0.7.1", + "log", + "serde", + "serde_json", +] + [[package]] name = "jsonrpc-client-transports" -version = "14.2.1" +version = "17.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2773fa94a2a1fd51efb89a8f45b8861023dbb415d18d3c9235ae9388d780f9ec" +checksum = "15b6c6ad01c7354d60de493148c30ac8a82b759e22ae678c8705e9b8e0c566a4" dependencies = [ - "failure", - "futures", + "derive_more", + "futures 0.3.12", "jsonrpc-core", "jsonrpc-pubsub", "jsonrpc-server-utils", @@ -1651,11 +1735,11 @@ dependencies = [ [[package]] name = "jsonrpc-core" -version = "14.2.0" +version = "17.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0747307121ffb9703afd93afbd0fb4f854c38fb873f2c8b90e0e902f27c7b62" +checksum = "07569945133257ff557eb37b015497104cea61a2c9edaf126c1cbd6e8332397f" dependencies = [ - "futures", + "futures 0.3.12", "log", "serde", "serde_derive", @@ -1664,18 +1748,19 @@ dependencies = [ [[package]] name = "jsonrpc-core-client" -version = "14.2.0" +version = "17.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34221123bc79b66279a3fde2d3363553835b43092d629b34f2e760c44dc94713" +checksum = "7ac9d56dc729912796637c30f475bbf834594607b27740dfea6e5fa7ba40d1f1" dependencies = [ + "futures 0.3.12", "jsonrpc-client-transports", ] [[package]] name = "jsonrpc-derive" -version = "14.2.1" +version = "17.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fadf6945e227246825a583514534d864554e9f23d80b3c77d034b10983db5ef" +checksum = "b68ba7e76e5c7796cfa4d2a30e83986550c34404c6d40551c902ca6f7bd4a137" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1685,44 +1770,48 @@ dependencies = [ [[package]] name = "jsonrpc-ipc-server" -version = "14.0.3" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b579cd0840d7db3ebaadf52f6f31ec601a260e78d610e44f68634f919e34497a" +checksum = "14c4cd89e5ea7e7f0884e828fc35bb83591a371b92439675eae28efa66c24a97" dependencies = [ + "futures 0.3.12", "jsonrpc-core", "jsonrpc-server-utils", "log", "parity-tokio-ipc", - "parking_lot 0.9.0", - "tokio-service", + "parking_lot", + "tower-service", ] [[package]] name = "jsonrpc-pubsub" -version = "14.2.0" +version = "17.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d44f5602a11d657946aac09357956d2841299ed422035edf140c552cb057986" +checksum = "0c48dbebce7a9c88ab272a4db7d6478aa4c6d9596e6c086366e89efc4e9ed89e" dependencies = [ + "futures 0.3.12", "jsonrpc-core", + "lazy_static", "log", - "parking_lot 0.10.2", + "parking_lot", "rand", "serde", ] [[package]] name = "jsonrpc-server-utils" -version = "14.2.0" +version = "17.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56cbfb462e7f902e21121d9f0d1c2b77b2c5b642e1a4e8f4ebfa2e15b94402bb" +checksum = "f4207cce738bf713a82525065b750a008f28351324f438f56b33d698ada95bb4" dependencies = [ "bytes", + "futures 0.3.12", "globset", "jsonrpc-core", "lazy_static", "log", "tokio", - "tokio-codec", + "tokio-util", "unicase", ] @@ -1750,18 +1839,18 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.79" +version = "0.2.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" +checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3" dependencies = [ "rustc-std-workspace-core", ] [[package]] name = "libgit2-sys" -version = "0.12.16+1.1.0" +version = "0.12.18+1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f91b2f931ee975a98155195be8cd82d02e8e029d7d793d2bac1b8181ac97020" +checksum = "3da6a42da88fc37ee1ecda212ffa254c25713532980005d5f7c0b0fbe7e6e885" dependencies = [ "cc", "libc", @@ -1830,15 +1919,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard", -] - [[package]] name = "lock_api" version = "0.4.1" @@ -1859,13 +1939,13 @@ dependencies = [ [[package]] name = "lsp-codec" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "169d737ad89cf8ddd82d1804d9122f54568c49377665157277cc90d747b1d31a" +checksum = "d33c83e320715a1e7e0466a53db2238becb2e5c446deff5506abc81aeacc5ec4" dependencies = [ "bytes", "serde_json", - "tokio-codec", + "tokio-util", ] [[package]] @@ -1973,9 +2053,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21251d3eb9ca5e8ac5b73384ddaa483a9bbc7d7dcd656b1fa8f266634810334a" +checksum = "b3d948b64449003363127ed6c6139f03273982c3fe97da4cb3dee933e38ce38f" dependencies = [ "ammonia", "anyhow", @@ -2004,7 +2084,7 @@ version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22bf8d885d073610aee20e7fa205c4341ed32a761dbde96da5fd96301a8d3e82" dependencies = [ - "parking_lot 0.11.0", + "parking_lot", "rustc-hash", "smallvec 1.4.2", ] @@ -2343,43 +2423,20 @@ dependencies = [ [[package]] name = "parity-tokio-ipc" -version = "0.2.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8281bf4f1d6429573f89589bf68d89451c46750977a8264f8ea3edbabeba7947" +checksum = "fd7f6c69d7687501b2205fe51ade1d7b8797bb3aa141fe5bf13dd78c0483bc89" dependencies = [ - "bytes", - "futures", + "futures 0.3.12", + "libc", "log", "mio-named-pipes", "miow 0.3.6", "rand", "tokio", - "tokio-named-pipes", - "tokio-uds", "winapi 0.3.9", ] -[[package]] -name = "parking_lot" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -dependencies = [ - "lock_api 0.3.4", - "parking_lot_core 0.6.2", - "rustc_version", -] - -[[package]] -name = "parking_lot" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" -dependencies = [ - "lock_api 0.3.4", - "parking_lot_core 0.7.2", -] - [[package]] name = "parking_lot" version = "0.11.0" @@ -2387,37 +2444,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" dependencies = [ "instant", - "lock_api 0.4.1", - "parking_lot_core 0.8.0", -] - -[[package]] -name = "parking_lot_core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -dependencies = [ - "cfg-if 0.1.10", - "cloudabi 0.0.3", - "libc", - "redox_syscall", - "rustc_version", - "smallvec 0.6.13", - "winapi 0.3.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" -dependencies = [ - "cfg-if 0.1.10", - "cloudabi 0.0.3", - "libc", - "redox_syscall", - "smallvec 1.4.2", - "winapi 0.3.9", + "lock_api", + "parking_lot_core", ] [[package]] @@ -2427,7 +2455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" dependencies = [ "cfg-if 0.1.10", - "cloudabi 0.1.0", + "cloudabi", "instant", "libc", "redox_syscall", @@ -2544,6 +2572,24 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" + +[[package]] +name = "pin-project-lite" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.18" @@ -2628,6 +2674,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + [[package]] name = "proc-macro2" version = "1.0.19" @@ -2720,9 +2778,9 @@ dependencies = [ [[package]] name = "racer" -version = "2.1.41" +version = "2.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f1a4baaaf5c4a9aa30c708c339ae293d02976d2b7f1575a59f44558d25bfea" +checksum = "7972a124e2b24dce35eb19f81eced829faec0e8227a7d744bbb1089934d05399" dependencies = [ "bitflags", "clap", @@ -2854,9 +2912,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.3.9" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ "aho-corasick", "memchr", @@ -2876,9 +2934,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.18" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "remote-test-client" @@ -2908,7 +2966,7 @@ dependencies = [ "crossbeam-channel", "difference", "env_logger 0.7.1", - "futures", + "futures 0.3.12", "heck", "home", "itertools 0.8.2", @@ -2938,8 +2996,7 @@ dependencies = [ "serde_json", "tempfile", "tokio", - "tokio-process", - "tokio-timer", + "tokio-util", "toml", "url 2.1.1", "walkdir", @@ -2964,9 +3021,9 @@ dependencies = [ [[package]] name = "rls-data" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c72ea97e045be5f6290bb157ebdc5ee9f2b093831ff72adfaf59025cf5c491" +checksum = "a58135eb039f3a3279a33779192f0ee78b56f57ae636e25cec83530e41debb99" dependencies = [ "rls-span", "serde", @@ -2990,7 +3047,7 @@ version = "0.6.0" dependencies = [ "clippy_lints", "env_logger 0.7.1", - "futures", + "futures 0.3.12", "log", "rand", "rls-data", @@ -3001,9 +3058,9 @@ dependencies = [ [[package]] name = "rls-span" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2e9bed56f6272bd85d9d06d1aaeef80c5fddc78a82199eb36dceb5f94e7d934" +checksum = "f0eea58478fc06e15f71b03236612173a1b81e9770314edecfa664375e3e4c86" dependencies = [ "serde", ] @@ -3049,18 +3106,18 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_arena" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f7b9bc5a6f79b1f230833cb4c8f8928d48c129b21df5b372c202fb826c0b5e" +checksum = "93575affa286089b92c8208aea4e60fe9fdd251a619a09b566d6e4e2cc123212" dependencies = [ "smallvec 1.4.2", ] [[package]] name = "rustc-ap-rustc_ast" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d77f313e9f30af93f2737f1a99d6552e26b702c5cef3bb65e35f5b4fe5191f1" +checksum = "4c700f2d3b25aa8d6446dd2936048737b08b2d547bd86e2a70afa9fee4e9c522" dependencies = [ "bitflags", "rustc-ap-rustc_data_structures", @@ -3075,9 +3132,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_ast_passes" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30408fbf42fa6fbeb383d3fce0f24d2490c3d12527beb2f48e6e728765bc8695" +checksum = "8e01f63e5259ee397bbe2e395d34a2e6b6b24f10c184d30fbbee1dcd7117f4f3" dependencies = [ "itertools 0.9.0", "rustc-ap-rustc_ast", @@ -3094,21 +3151,20 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_ast_pretty" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d47b8a3adcccc204578b0ee9cd2f9952921fa43977f58343913cca04cce87043" +checksum = "99d644c69c55deb24257cb0cb5261265fe5134f6f545e9062e1c18b07e422c68" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_span", - "rustc-ap-rustc_target", "tracing", ] [[package]] name = "rustc-ap-rustc_attr" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66f5f53ecdbf7d8b47905936f93eb1fdae496137e94b7e4023a0b866b0e1a92d" +checksum = "797fc68816d5396870f04e03d35164f5275d2502403239d4caec7ce063683f41" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", @@ -3120,14 +3176,13 @@ dependencies = [ "rustc-ap-rustc_serialize", "rustc-ap-rustc_session", "rustc-ap-rustc_span", - "version_check", ] [[package]] name = "rustc-ap-rustc_data_structures" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aa913fa40b90157067b17dd7ddfd5df0d8566e339ffa8351a638bdf3fc7ee81" +checksum = "5d840c4e6198b57982a54543ae604d634c7ceb7107f0c75970b88ebaff077ac5" dependencies = [ "arrayvec", "bitflags", @@ -3138,7 +3193,7 @@ dependencies = [ "jobserver", "libc", "measureme", - "parking_lot 0.11.0", + "parking_lot", "rustc-ap-rustc_graphviz", "rustc-ap-rustc_index", "rustc-ap-rustc_macros", @@ -3156,9 +3211,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_errors" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4b4956287d7c4996409b8362aa69c0c9a6853751ff00ee0a6f78223c5ef3ad" +checksum = "2f2f99bdc828ad417636d9016611dc9047b641fadcb7f533b8b0e9616d81f90b" dependencies = [ "annotate-snippets 0.8.0", "atty", @@ -3176,9 +3231,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_expand" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa908bb1b67230dd4309e93edefc6a6c2f3d8b6a195f77c47743c882114a22e" +checksum = "27008b4c7ded287bf5cb20b84d6d5a6566329140f2e2bc8f6e68b37a34898595" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_passes", @@ -3188,6 +3243,7 @@ dependencies = [ "rustc-ap-rustc_errors", "rustc-ap-rustc_feature", "rustc-ap-rustc_lexer", + "rustc-ap-rustc_lint_defs", "rustc-ap-rustc_macros", "rustc-ap-rustc_parse", "rustc-ap-rustc_serialize", @@ -3199,9 +3255,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_feature" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b7a1db115893ed7ed0db80f70d2246c1709de7854238acde76471495930f2a" +checksum = "6bb47b53670f1263ed1389dda932d5b5a6daf98579c1f076c2ee7d7f22709b7c" dependencies = [ "rustc-ap-rustc_data_structures", "rustc-ap-rustc_span", @@ -3209,21 +3265,21 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_fs_util" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937887cb606cc72193ea3c5feb8bbbb810d812aa233b9a1e7749155c4a3501" +checksum = "cdaddc4bae5ffab17037553e172f5014686db600050429aaa60aec14fe780e84" [[package]] name = "rustc-ap-rustc_graphviz" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e39e179e616356927f0c4eda43e3a35d88476f91e1ac8e4a0a09661dbab44a6e" +checksum = "3d73c72543311e88786f7380a3bfd946395579c1a0c0441a879a97fcdea79130" [[package]] name = "rustc-ap-rustc_index" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572d3962d6999f3b1a71d335308e939e204339d4ad36e6ebe7a591c9d4329f5d" +checksum = "bba8d74ed4bad44a5b4264cf2a51ad0bd458ed56caa5bb090e989b8002ec6327" dependencies = [ "arrayvec", "rustc-ap-rustc_macros", @@ -3232,32 +3288,33 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_lexer" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bc89d9ca7a78fb82e103b389362c55f03800745f8ba14e068b805cfaf783ec" +checksum = "3a030d00510966cd31e13dca5e6c1bd40d303a932c54eca40e854188bca8c49e" dependencies = [ "unicode-xid", ] [[package]] name = "rustc-ap-rustc_lint_defs" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d39bda92aabd77e49ac8ad5e24fccf9d7245b8ff2bf1249ab98733e2e5a2863" +checksum = "bdff95da1b5d979183ef5c285817ba6cc67a1ac11296ef1e87b1b5bbaf57213c" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_macros", "rustc-ap-rustc_serialize", "rustc-ap-rustc_span", + "rustc-ap-rustc_target", "tracing", ] [[package]] name = "rustc-ap-rustc_macros" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3295fbc9625197494e356e92d8ac08370eddafa60189861c7b2f084b3b5a6b8" +checksum = "fe3ed7401bf6f5a256d58cd0e1c1e2e77eec25e60a0d7ad75313962edcb4e396" dependencies = [ "proc-macro2", "quote", @@ -3267,9 +3324,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_parse" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff5d0094396844efead43303a6eb25b8a4962e2c80fb0ea4a86e4101fbfd404" +checksum = "609a624baffa3f99847d57d30c96ee6732ce0912f8df4be239b6fd91533910d6" dependencies = [ "bitflags", "rustc-ap-rustc_ast", @@ -3287,9 +3344,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_serialize" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5cff6709a8b51a3730288a9ead17cabe8146b1c787db52298447ef7890140a" +checksum = "bc232e2a351d8131c8f1386ce372ee22ef7b1b0b897bbf817a8ce4792029a564" dependencies = [ "indexmap", "smallvec 1.4.2", @@ -3297,9 +3354,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_session" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36bb15ef12174b5ed6419a7e4260a899ce8927e8c8fd1f0cddf178818737dcdf" +checksum = "18acf94c820cd0c64ee1cbd811fd1f4d5ba18987c457c88771359b90cb1a12f5" dependencies = [ "bitflags", "getopts", @@ -3319,9 +3376,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_span" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "104d349a32be9cfd3d39a5a70ad6c5e682ce262fc5cc8717d35a01e980c0d8b2" +checksum = "d3479f453a38b6a5572938d035fc2b3cb6ec379c57f598b8682b512eb90c7858" dependencies = [ "cfg-if 0.1.10", "md-5", @@ -3339,9 +3396,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_target" -version = "691.0.0" +version = "705.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d7ac4ded9a6aecb534744c836a160497985f0d53b272581e95e7890d31b9e17" +checksum = "78cacaf829778cf07bb97a9f4604896789de12392175f3743e74a30ed370f1c1" dependencies = [ "bitflags", "rustc-ap-rustc_data_structures", @@ -3518,7 +3575,6 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_span", - "rustc_target", "tracing", ] @@ -3536,7 +3592,6 @@ dependencies = [ "rustc_serialize", "rustc_session", "rustc_span", - "version_check", ] [[package]] @@ -3594,6 +3649,7 @@ version = "0.0.0" dependencies = [ "bitflags", "cc", + "itertools 0.9.0", "jobserver", "libc", "memmap", @@ -3632,7 +3688,7 @@ dependencies = [ "jobserver", "libc", "measureme", - "parking_lot 0.11.0", + "parking_lot", "rustc-hash", "rustc-rayon", "rustc-rayon-core", @@ -3652,6 +3708,7 @@ dependencies = [ name = "rustc_driver" version = "0.0.0" dependencies = [ + "atty", "libc", "rustc_ast", "rustc_ast_pretty", @@ -3714,6 +3771,7 @@ dependencies = [ "rustc_errors", "rustc_feature", "rustc_lexer", + "rustc_lint_defs", "rustc_macros", "rustc_parse", "rustc_serialize", @@ -3745,6 +3803,7 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_data_structures", + "rustc_feature", "rustc_index", "rustc_macros", "rustc_serialize", @@ -3894,6 +3953,7 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", + "rustc_target", "tracing", ] @@ -4098,6 +4158,7 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", + "rustc_trait_selection", "rustc_typeck", "tracing", ] @@ -4106,7 +4167,7 @@ dependencies = [ name = "rustc_query_system" version = "0.0.0" dependencies = [ - "parking_lot 0.11.0", + "parking_lot", "rustc-rayon-core", "rustc_arena", "rustc_data_structures", @@ -4277,6 +4338,7 @@ dependencies = [ "chalk-ir", "chalk-solve", "rustc_ast", + "rustc_attr", "rustc_data_structures", "rustc_hir", "rustc_index", @@ -4311,6 +4373,7 @@ dependencies = [ "bitflags", "rustc_data_structures", "rustc_index", + "rustc_macros", "rustc_serialize", ] @@ -4350,18 +4413,27 @@ dependencies = [ name = "rustdoc" version = "0.0.0" dependencies = [ + "arrayvec", "expect-test", "itertools 0.9.0", "minifier", "pulldown-cmark 0.8.0", "regex", "rustc-rayon", + "rustdoc-json-types", "serde", "serde_json", "smallvec 1.4.2", "tempfile", ] +[[package]] +name = "rustdoc-json-types" +version = "0.1.0" +dependencies = [ + "serde", +] + [[package]] name = "rustdoc-themes" version = "0.1.0" @@ -4397,7 +4469,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.4.30" +version = "1.4.36" dependencies = [ "annotate-snippets 0.6.1", "anyhow", @@ -4575,6 +4647,7 @@ version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" dependencies = [ + "indexmap", "itoa", "ryu", "serde", @@ -5034,242 +5107,50 @@ checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" [[package]] name = "tokio" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -dependencies = [ - "bytes", - "futures", - "mio", - "num_cpus", - "tokio-codec", - "tokio-current-thread", - "tokio-executor", - "tokio-fs", - "tokio-io", - "tokio-reactor", - "tokio-sync", - "tokio-tcp", - "tokio-threadpool", - "tokio-timer", - "tokio-udp", - "tokio-uds", -] - -[[package]] -name = "tokio-codec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" -dependencies = [ - "bytes", - "futures", - "tokio-io", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" -dependencies = [ - "futures", - "tokio-executor", -] - -[[package]] -name = "tokio-executor" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures", -] - -[[package]] -name = "tokio-fs" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" -dependencies = [ - "futures", - "tokio-io", - "tokio-threadpool", -] - -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -dependencies = [ - "bytes", - "futures", - "log", -] - -[[package]] -name = "tokio-named-pipes" -version = "0.1.0" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d282d483052288b2308ba5ee795f5673b159c9bdf63c385a05609da782a5eae" +checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" dependencies = [ "bytes", - "futures", - "mio", - "mio-named-pipes", - "tokio", -] - -[[package]] -name = "tokio-process" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382d90f43fa31caebe5d3bc6cfd854963394fff3b8cb59d5146607aaae7e7e43" -dependencies = [ - "crossbeam-queue 0.1.2", - "futures", + "futures-core", + "iovec", "lazy_static", "libc", - "log", + "memchr", "mio", "mio-named-pipes", - "tokio-io", - "tokio-reactor", - "tokio-signal", - "winapi 0.3.9", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures", - "lazy_static", - "log", - "mio", - "num_cpus", - "parking_lot 0.9.0", - "slab", - "tokio-executor", - "tokio-io", - "tokio-sync", -] - -[[package]] -name = "tokio-service" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" -dependencies = [ - "futures", -] - -[[package]] -name = "tokio-signal" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c34c6e548f101053321cba3da7cbb87a610b85555884c41b07da2eb91aff12" -dependencies = [ - "futures", - "libc", - "mio", "mio-uds", - "signal-hook-registry", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "winapi 0.3.9", -] - -[[package]] -name = "tokio-sync" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" -dependencies = [ - "fnv", - "futures", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" -dependencies = [ - "bytes", - "futures", - "iovec", - "mio", - "tokio-io", - "tokio-reactor", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" -dependencies = [ - "crossbeam-deque", - "crossbeam-queue 0.2.3", - "crossbeam-utils 0.7.2", - "futures", - "lazy_static", - "log", "num_cpus", + "pin-project-lite 0.1.11", + "signal-hook-registry", "slab", - "tokio-executor", -] - -[[package]] -name = "tokio-timer" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures", - "slab", - "tokio-executor", + "tokio-macros", + "winapi 0.3.9", ] [[package]] -name = "tokio-udp" -version = "0.1.6" +name = "tokio-macros" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ - "bytes", - "futures", - "log", - "mio", - "tokio-codec", - "tokio-io", - "tokio-reactor", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "tokio-uds" -version = "0.2.7" +name = "tokio-util" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" dependencies = [ "bytes", - "futures", - "iovec", - "libc", + "futures-core", + "futures-sink", "log", - "mio", - "mio-uds", - "tokio-codec", - "tokio-io", - "tokio-reactor", + "pin-project-lite 0.1.11", + "tokio", ] [[package]] @@ -5281,6 +5162,12 @@ dependencies = [ "serde", ] +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + [[package]] name = "tracing" version = "0.1.19" @@ -5343,7 +5230,7 @@ dependencies = [ "chrono", "lazy_static", "matchers", - "parking_lot 0.9.0", + "parking_lot", "regex", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 204c92045b..f961d3e9b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "compiler/rustc", "library/std", "library/test", + "src/rustdoc-json-types", "src/tools/cargotest", "src/tools/clippy", "src/tools/compiletest", @@ -31,6 +32,7 @@ members = [ "src/tools/rustdoc-themes", "src/tools/unicode-table-generator", "src/tools/expand-yaml-anchors", + "src/tools/jsondocck", ] exclude = [ @@ -104,11 +106,5 @@ rustc-std-workspace-core = { path = 'library/rustc-std-workspace-core' } rustc-std-workspace-alloc = { path = 'library/rustc-std-workspace-alloc' } rustc-std-workspace-std = { path = 'library/rustc-std-workspace-std' } -# This crate's integration with libstd is a bit wonky, so we use a submodule -# instead of a crates.io dependency. Make sure everything else in the repo is -# also using the submodule, however, so we can avoid duplicate copies of the -# source code for this crate. -backtrace = { path = "library/backtrace" } - [patch."https://github.com/rust-lang/rust-clippy"] clippy_lints = { path = "src/tools/clippy/clippy_lints" } diff --git a/README.md b/README.md index 07c0960466..6ab11e7e2b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ - -The Rust Programming Language - +# The Rust Programming Language This is the main source code repository for [Rust]. It contains the compiler, standard library, and documentation. diff --git a/RELEASES.md b/RELEASES.md index 18492213a5..314482c971 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,174 @@ +Version 1.51.0 (2021-03-25) +============================ + +Language +-------- +- [You can now parameterize items such as functions, traits, and `struct`s by constant + values in addition to by types and lifetimes.][79135] Also known as "const generics" + E.g. you can now write the following. Note: Only values of primitive integers, + `bool`, or `char` types are currently permitted. + ```rust + struct GenericArray { + inner: [T; LENGTH] + } + + impl GenericArray { + const fn last(&self) -> Option<&T> { + if LENGTH == 0 { + None + } else { + Some(&self.inner[LENGTH - 1]) + } + } + } + ``` + + +Compiler +-------- + +- [Added the `-Csplit-debuginfo` codegen option for macOS platforms.][79570] + This option controls whether debug information is split across multiple files + or packed into a single file. **Note** This option is unstable on other platforms. +- [Added tier 3\* support for `aarch64_be-unknown-linux-gnu`, + `aarch64-unknown-linux-gnu_ilp32`, and `aarch64_be-unknown-linux-gnu_ilp32` targets.][81455] +- [Added tier 3 support for `i386-unknown-linux-gnu` and `i486-unknown-linux-gnu` targets.][80662] +- [The `target-cpu=native` option will now detect individual features of CPUs.][80749] +- [Rust now uses `inline-asm` for stack probes when used with LLVM 11.0.1+][77885] + +\* Refer to Rust's [platform support page][forge-platform-support] for more +information on Rust's tiered platform support. + +Libraries +--------- + +- [`Box::downcast` is now also implemented for any `dyn Any + Send + Sync` object.][80945] +- [`str` now implements `AsMut`.][80279] +- [`u64` and `u128` now implement `From`.][79502] +- [`Error` is now implemented for `&T` where `T` implements `Error`.][75180] +- [`Poll::{map_ok, map_err}` are now implemented for `Poll>>`.][80968] +- [`unsigned_abs` is now implemented for all signed integer types.][80959] +- [`io::Empty` now implements `io::Seek`.][78044] +- [`rc::Weak` and `sync::Weak`'s methods such as `as_ptr` are now implemented for + `T: ?Sized` types.][80764] + +Stabilized APIs +--------------- + +- [`Arc::decrement_strong_count`] +- [`Arc::increment_strong_count`] +- [`Once::call_once_force`] +- [`Peekable::next_if_eq`] +- [`Peekable::next_if`] +- [`Seek::stream_position`] +- [`array::IntoIter`] +- [`panic::panic_any`] +- [`ptr::addr_of!`] +- [`ptr::addr_of_mut!`] +- [`slice::fill_with`] +- [`slice::split_inclusive_mut`] +- [`slice::split_inclusive`] +- [`slice::strip_prefix`] +- [`slice::strip_suffix`] +- [`str::split_inclusive`] +- [`sync::OnceState`] +- [`task::Wake`] + +Cargo +----- +- [Added the `split-debuginfo` profile option to control the -Csplit-debuginfo + codegen option.][cargo/9112] +- [Added the `resolver` field to `Cargo.toml` to enable the new feature resolver + and CLI option behavior.][cargo/8997] Version 2 of the feature resolver will try + to avoid unifying features of dependencies where that unification could be unwanted. + Such as using the same dependency with a `std` feature in a build scripts and + proc-macros, while using the `no-std` feature in the final binary. See the + [Cargo book documentation][feature-resolver@2.0] for more information on the feature. + +Rustdoc +------- + +- [Rustdoc will now include documentation for methods available from `Deref` traits.][80653] +- [You can now provide a `--default-theme` flag which sets the default theme to use for + documentation.][79642] + +Various improvements to intra-doc links: + +- [You can link to non-path primitives such as `slice`.][80181] +- [You can link to associated items.][74489] +- [You can now include generic parameters when linking to items, like `Vec`.][76934] + +Misc +---- +- [You can now pass `--include-ignored` to tests (e.g. with + `cargo test -- --include-ignored`) to include testing tests marked `#[ignore]`.][80053] + +Compatibility Notes +------------------- + +- [WASI platforms no longer use the `wasm-bindgen` ABI, and instead use the wasm32 ABI.][79998] +- [`rustc` no longer promotes division, modulo and indexing operations to `const` that + could fail.][80579] +- [The minimum version of glibc for the following platforms has been bumped to version 2.31 + for the distributed artifacts.][81521] + - `armv5te-unknown-linux-gnueabi` + - `sparc64-unknown-linux-gnu` + - `thumbv7neon-unknown-linux-gnueabihf` + - `armv7-unknown-linux-gnueabi` + - `x86_64-unknown-linux-gnux32` + +Internal Only +------------- + +- [Consistently avoid constructing optimized MIR when not doing codegen][80718] + +[79135]: https://github.com/rust-lang/rust/pull/79135 +[74489]: https://github.com/rust-lang/rust/pull/74489 +[76934]: https://github.com/rust-lang/rust/pull/76934 +[79570]: https://github.com/rust-lang/rust/pull/79570 +[80181]: https://github.com/rust-lang/rust/pull/80181 +[79642]: https://github.com/rust-lang/rust/pull/79642 +[80945]: https://github.com/rust-lang/rust/pull/80945 +[80279]: https://github.com/rust-lang/rust/pull/80279 +[80053]: https://github.com/rust-lang/rust/pull/80053 +[79502]: https://github.com/rust-lang/rust/pull/79502 +[75180]: https://github.com/rust-lang/rust/pull/75180 +[79135]: https://github.com/rust-lang/rust/pull/79135 +[81521]: https://github.com/rust-lang/rust/pull/81521 +[80968]: https://github.com/rust-lang/rust/pull/80968 +[80959]: https://github.com/rust-lang/rust/pull/80959 +[80718]: https://github.com/rust-lang/rust/pull/80718 +[80653]: https://github.com/rust-lang/rust/pull/80653 +[80579]: https://github.com/rust-lang/rust/pull/80579 +[79998]: https://github.com/rust-lang/rust/pull/79998 +[78044]: https://github.com/rust-lang/rust/pull/78044 +[81455]: https://github.com/rust-lang/rust/pull/81455 +[80764]: https://github.com/rust-lang/rust/pull/80764 +[80749]: https://github.com/rust-lang/rust/pull/80749 +[80662]: https://github.com/rust-lang/rust/pull/80662 +[77885]: https://github.com/rust-lang/rust/pull/77885 +[cargo/8997]: https://github.com/rust-lang/cargo/pull/8997 +[cargo/9112]: https://github.com/rust-lang/cargo/pull/9112 +[feature-resolver@2.0]: https://doc.rust-lang.org/nightly/cargo/reference/features.html#feature-resolver-version-2 +[`Once::call_once_force`]: https://doc.rust-lang.org/stable/std/sync/struct.Once.html#method.call_once_force +[`sync::OnceState`]: https://doc.rust-lang.org/stable/std/sync/struct.OnceState.html +[`panic::panic_any`]: https://doc.rust-lang.org/stable/std/panic/fn.panic_any.html +[`slice::strip_prefix`]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.strip_prefix +[`slice::strip_suffix`]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.strip_prefix +[`Arc::increment_strong_count`]: https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.increment_strong_count +[`Arc::decrement_strong_count`]: https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.decrement_strong_count +[`slice::fill_with`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.fill_with +[`ptr::addr_of!`]: https://doc.rust-lang.org/nightly/std/ptr/macro.addr_of.html +[`ptr::addr_of_mut!`]: https://doc.rust-lang.org/nightly/std/ptr/macro.addr_of_mut.html +[`array::IntoIter`]: https://doc.rust-lang.org/nightly/std/array/struct.IntoIter.html +[`slice::split_inclusive`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_inclusive +[`slice::split_inclusive_mut`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.split_inclusive_mut +[`str::split_inclusive`]: https://doc.rust-lang.org/nightly/std/primitive.str.html#method.split_inclusive +[`task::Wake`]: https://doc.rust-lang.org/nightly/std/task/trait.Wake.html +[`Seek::stream_position`]: https://doc.rust-lang.org/nightly/std/io/trait.Seek.html#method.stream_position +[`Peekable::next_if`]: https://doc.rust-lang.org/nightly/std/iter/struct.Peekable.html#method.next_if +[`Peekable::next_if_eq`]: https://doc.rust-lang.org/nightly/std/iter/struct.Peekable.html#method.next_if_eq + Version 1.50.0 (2021-02-11) ============================ diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index f468bad635..651f4c6fab 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -11,12 +11,10 @@ html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/", test(no_crate_inject, attr(deny(warnings))) )] -#![feature(array_value_iter_slice)] #![feature(dropck_eyepatch)] #![feature(new_uninit)] #![feature(maybe_uninit_slice)] -#![feature(array_value_iter)] -#![feature(min_const_generics)] +#![cfg_attr(bootstrap, feature(min_const_generics))] #![feature(min_specialization)] #![cfg_attr(test, feature(test))] @@ -32,7 +30,7 @@ use std::slice; #[inline(never)] #[cold] -pub fn cold_path R, R>(f: F) -> R { +fn cold_path R, R>(f: F) -> R { f() } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 220bbed7e7..2ddcb9ef84 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -23,8 +23,8 @@ pub use GenericArgs::*; pub use UnsafeSource::*; use crate::ptr::P; -use crate::token::{self, CommentKind, DelimToken}; -use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream}; +use crate::token::{self, CommentKind, DelimToken, Token}; +use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -167,10 +167,7 @@ pub enum GenericArgs { impl GenericArgs { pub fn is_angle_bracketed(&self) -> bool { - match *self { - AngleBracketed(..) => true, - _ => false, - } + matches!(self, AngleBracketed(..)) } pub fn span(&self) -> Span { @@ -245,12 +242,21 @@ impl Into>> for ParenthesizedArgs { /// A path like `Foo(A, B) -> C`. #[derive(Clone, Encodable, Decodable, Debug)] pub struct ParenthesizedArgs { - /// Overall span + /// ```text + /// Foo(A, B) -> C + /// ^^^^^^^^^^^^^^ + /// ``` pub span: Span, /// `(A, B)` pub inputs: Vec>, + /// ```text + /// Foo(A, B) -> C + /// ^^^^^^ + /// ``` + pub inputs_span: Span, + /// `C` pub output: FnRetTy, } @@ -371,6 +377,8 @@ pub enum GenericParamKind { ty: P, /// Span of the `const` keyword. kw_span: Span, + /// Optional default value for the const generic param + default: Option, }, } @@ -434,9 +442,9 @@ pub enum WherePredicate { impl WherePredicate { pub fn span(&self) -> Span { match self { - &WherePredicate::BoundPredicate(ref p) => p.span, - &WherePredicate::RegionPredicate(ref p) => p.span, - &WherePredicate::EqPredicate(ref p) => p.span, + WherePredicate::BoundPredicate(p) => p.span, + WherePredicate::RegionPredicate(p) => p.span, + WherePredicate::EqPredicate(p) => p.span, } } } @@ -629,23 +637,20 @@ impl Pat { /// Is this a `..` pattern? pub fn is_rest(&self) -> bool { - match self.kind { - PatKind::Rest => true, - _ => false, - } + matches!(self.kind, PatKind::Rest) } } -/// A single field in a struct pattern +/// A single field in a struct pattern. /// -/// Patterns like the fields of Foo `{ x, ref y, ref mut z }` -/// are treated the same as` x: x, y: ref y, z: ref mut z`, -/// except is_shorthand is true +/// Patterns like the fields of `Foo { x, ref y, ref mut z }` +/// are treated the same as `x: x, y: ref y, z: ref mut z`, +/// except when `is_shorthand` is true. #[derive(Clone, Encodable, Decodable, Debug)] pub struct FieldPat { - /// The identifier for the field + /// The identifier for the field. pub ident: Ident, - /// The pattern the field is destructured to + /// The pattern the field is destructured to. pub pat: P, pub is_shorthand: bool, pub attrs: AttrVec, @@ -852,10 +857,7 @@ impl BinOpKind { } } pub fn lazy(&self) -> bool { - match *self { - BinOpKind::And | BinOpKind::Or => true, - _ => false, - } + matches!(self, BinOpKind::And | BinOpKind::Or) } pub fn is_comparison(&self) -> bool { @@ -923,16 +925,6 @@ impl Stmt { } } - pub fn set_tokens(&mut self, tokens: Option) { - match self.kind { - StmtKind::Local(ref mut local) => local.tokens = tokens, - StmtKind::Item(ref mut item) => item.tokens = tokens, - StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => expr.tokens = tokens, - StmtKind::Empty => {} - StmtKind::MacCall(ref mut mac) => mac.tokens = tokens, - } - } - pub fn has_trailing_semicolon(&self) -> bool { match &self.kind { StmtKind::Semi(_) => true, @@ -963,17 +955,11 @@ impl Stmt { } pub fn is_item(&self) -> bool { - match self.kind { - StmtKind::Item(_) => true, - _ => false, - } + matches!(self.kind, StmtKind::Item(_)) } pub fn is_expr(&self) -> bool { - match self.kind { - StmtKind::Expr(_) => true, - _ => false, - } + matches!(self.kind, StmtKind::Expr(_)) } } @@ -1107,15 +1093,9 @@ impl Expr { if let ExprKind::Block(ref block, _) = self.kind { match block.stmts.last().map(|last_stmt| &last_stmt.kind) { // Implicit return - Some(&StmtKind::Expr(_)) => true, - Some(&StmtKind::Semi(ref expr)) => { - if let ExprKind::Ret(_) = expr.kind { - // Last statement is explicit return. - true - } else { - false - } - } + Some(StmtKind::Expr(_)) => true, + // Last statement is an explicit return? + Some(StmtKind::Semi(expr)) => matches!(expr.kind, ExprKind::Ret(_)), // This is a block that doesn't end in either an implicit or explicit return. _ => false, } @@ -1128,7 +1108,7 @@ impl Expr { /// Is this expr either `N`, or `{ N }`. /// /// If this is not the case, name resolution does not resolve `N` when using - /// `feature(min_const_generics)` as more complex expressions are not supported. + /// `min_const_generics` as more complex expressions are not supported. pub fn is_potential_trivial_const_param(&self) -> bool { let this = if let ExprKind::Block(ref block, None) = self.kind { if block.stmts.len() == 1 { @@ -1483,8 +1463,8 @@ pub enum MacArgs { Eq( /// Span of the `=` token. Span, - /// Token stream of the "value". - TokenStream, + /// "value" as a nonterminal token. + Token, ), } @@ -1497,10 +1477,10 @@ impl MacArgs { } pub fn span(&self) -> Option { - match *self { + match self { MacArgs::Empty => None, MacArgs::Delimited(dspan, ..) => Some(dspan.entire()), - MacArgs::Eq(eq_span, ref tokens) => Some(eq_span.to(tokens.span().unwrap_or(eq_span))), + MacArgs::Eq(eq_span, token) => Some(eq_span.to(token.span)), } } @@ -1509,7 +1489,8 @@ impl MacArgs { pub fn inner_tokens(&self) -> TokenStream { match self { MacArgs::Empty => TokenStream::default(), - MacArgs::Delimited(.., tokens) | MacArgs::Eq(.., tokens) => tokens.clone(), + MacArgs::Delimited(.., tokens) => tokens.clone(), + MacArgs::Eq(.., token) => TokenTree::Token(token.clone()).into(), } } @@ -1652,26 +1633,17 @@ pub enum LitKind { impl LitKind { /// Returns `true` if this literal is a string. pub fn is_str(&self) -> bool { - match *self { - LitKind::Str(..) => true, - _ => false, - } + matches!(self, LitKind::Str(..)) } /// Returns `true` if this literal is byte literal string. pub fn is_bytestr(&self) -> bool { - match self { - LitKind::ByteStr(_) => true, - _ => false, - } + matches!(self, LitKind::ByteStr(_)) } /// Returns `true` if this is a numeric literal. pub fn is_numeric(&self) -> bool { - match *self { - LitKind::Int(..) | LitKind::Float(..) => true, - _ => false, - } + matches!(self, LitKind::Int(..) | LitKind::Float(..)) } /// Returns `true` if this literal has no suffix. @@ -1974,7 +1946,7 @@ impl TyKind { } pub fn is_unit(&self) -> bool { - if let TyKind::Tup(ref tys) = *self { tys.is_empty() } else { false } + matches!(self, TyKind::Tup(tys) if tys.is_empty()) } } @@ -2237,10 +2209,7 @@ impl FnDecl { self.inputs.get(0).map_or(false, Param::is_self) } pub fn c_variadic(&self) -> bool { - self.inputs.last().map_or(false, |arg| match arg.ty.kind { - TyKind::CVarArgs => true, - _ => false, - }) + self.inputs.last().map_or(false, |arg| matches!(arg.ty.kind, TyKind::CVarArgs)) } } @@ -2686,6 +2655,36 @@ impl Default for FnHeader { } } +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct TraitKind( + pub IsAuto, + pub Unsafe, + pub Generics, + pub GenericBounds, + pub Vec>, +); + +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct TyAliasKind(pub Defaultness, pub Generics, pub GenericBounds, pub Option>); + +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct ImplKind { + pub unsafety: Unsafe, + pub polarity: ImplPolarity, + pub defaultness: Defaultness, + pub constness: Const, + pub generics: Generics, + + /// The trait being implemented, if any. + pub of_trait: Option, + + pub self_ty: P, + pub items: Vec>, +} + +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct FnKind(pub Defaultness, pub FnSig, pub Generics, pub Option>); + #[derive(Clone, Encodable, Decodable, Debug)] pub enum ItemKind { /// An `extern crate` item, with the optional *original* crate name if the crate was renamed. @@ -2707,7 +2706,7 @@ pub enum ItemKind { /// A function declaration (`fn`). /// /// E.g., `fn foo(bar: usize) -> usize { .. }`. - Fn(Defaultness, FnSig, Generics, Option>), + Fn(Box), /// A module declaration (`mod`). /// /// E.g., `mod foo;` or `mod foo { .. }`. @@ -2721,7 +2720,7 @@ pub enum ItemKind { /// A type alias (`type`). /// /// E.g., `type Foo = Bar;`. - TyAlias(Defaultness, Generics, GenericBounds, Option>), + TyAlias(Box), /// An enum definition (`enum`). /// /// E.g., `enum Foo { C, D }`. @@ -2737,7 +2736,7 @@ pub enum ItemKind { /// A trait declaration (`trait`). /// /// E.g., `trait Foo { .. }`, `trait Foo { .. }` or `auto trait Foo {}`. - Trait(IsAuto, Unsafe, Generics, GenericBounds, Vec>), + Trait(Box), /// Trait alias /// /// E.g., `trait Foo = Bar + Quux;`. @@ -2745,19 +2744,7 @@ pub enum ItemKind { /// An implementation. /// /// E.g., `impl Foo { .. }` or `impl Trait for Foo { .. }`. - Impl { - unsafety: Unsafe, - polarity: ImplPolarity, - defaultness: Defaultness, - constness: Const, - generics: Generics, - - /// The trait being implemented, if any. - of_trait: Option, - - self_ty: P, - items: Vec>, - }, + Impl(Box), /// A macro invocation. /// /// E.g., `foo!(..)`. @@ -2767,6 +2754,9 @@ pub enum ItemKind { MacroDef(MacroDef), } +#[cfg(target_arch = "x86_64")] +rustc_data_structures::static_assert_size!(ItemKind, 112); + impl ItemKind { pub fn article(&self) -> &str { use ItemKind::*; @@ -2801,14 +2791,14 @@ impl ItemKind { pub fn generics(&self) -> Option<&Generics> { match self { - Self::Fn(_, _, generics, _) - | Self::TyAlias(_, generics, ..) + Self::Fn(box FnKind(_, _, generics, _)) + | Self::TyAlias(box TyAliasKind(_, generics, ..)) | Self::Enum(_, generics) | Self::Struct(_, generics) | Self::Union(_, generics) - | Self::Trait(_, _, generics, ..) + | Self::Trait(box TraitKind(_, _, generics, ..)) | Self::TraitAlias(generics, _) - | Self::Impl { generics, .. } => Some(generics), + | Self::Impl(box ImplKind { generics, .. }) => Some(generics), _ => None, } } @@ -2831,17 +2821,22 @@ pub enum AssocItemKind { /// If `def` is parsed, then the constant is provided, and otherwise required. Const(Defaultness, P, Option>), /// An associated function. - Fn(Defaultness, FnSig, Generics, Option>), + Fn(Box), /// An associated type. - TyAlias(Defaultness, Generics, GenericBounds, Option>), + TyAlias(Box), /// A macro expanding to associated items. MacCall(MacCall), } +#[cfg(target_arch = "x86_64")] +rustc_data_structures::static_assert_size!(AssocItemKind, 72); + impl AssocItemKind { pub fn defaultness(&self) -> Defaultness { match *self { - Self::Const(def, ..) | Self::Fn(def, ..) | Self::TyAlias(def, ..) => def, + Self::Const(def, ..) + | Self::Fn(box FnKind(def, ..)) + | Self::TyAlias(box TyAliasKind(def, ..)) => def, Self::MacCall(..) => Defaultness::Final, } } @@ -2851,8 +2846,8 @@ impl From for ItemKind { fn from(assoc_item_kind: AssocItemKind) -> ItemKind { match assoc_item_kind { AssocItemKind::Const(a, b, c) => ItemKind::Const(a, b, c), - AssocItemKind::Fn(a, b, c, d) => ItemKind::Fn(a, b, c, d), - AssocItemKind::TyAlias(a, b, c, d) => ItemKind::TyAlias(a, b, c, d), + AssocItemKind::Fn(fn_kind) => ItemKind::Fn(fn_kind), + AssocItemKind::TyAlias(ty_alias_kind) => ItemKind::TyAlias(ty_alias_kind), AssocItemKind::MacCall(a) => ItemKind::MacCall(a), } } @@ -2864,8 +2859,8 @@ impl TryFrom for AssocItemKind { fn try_from(item_kind: ItemKind) -> Result { Ok(match item_kind { ItemKind::Const(a, b, c) => AssocItemKind::Const(a, b, c), - ItemKind::Fn(a, b, c, d) => AssocItemKind::Fn(a, b, c, d), - ItemKind::TyAlias(a, b, c, d) => AssocItemKind::TyAlias(a, b, c, d), + ItemKind::Fn(fn_kind) => AssocItemKind::Fn(fn_kind), + ItemKind::TyAlias(ty_alias_kind) => AssocItemKind::TyAlias(ty_alias_kind), ItemKind::MacCall(a) => AssocItemKind::MacCall(a), _ => return Err(item_kind), }) @@ -2877,20 +2872,23 @@ impl TryFrom for AssocItemKind { pub enum ForeignItemKind { /// A foreign static item (`static FOO: u8`). Static(P, Mutability, Option>), - /// A foreign function. - Fn(Defaultness, FnSig, Generics, Option>), - /// A foreign type. - TyAlias(Defaultness, Generics, GenericBounds, Option>), + /// An foreign function. + Fn(Box), + /// An foreign type. + TyAlias(Box), /// A macro expanding to foreign items. MacCall(MacCall), } +#[cfg(target_arch = "x86_64")] +rustc_data_structures::static_assert_size!(ForeignItemKind, 72); + impl From for ItemKind { fn from(foreign_item_kind: ForeignItemKind) -> ItemKind { match foreign_item_kind { ForeignItemKind::Static(a, b, c) => ItemKind::Static(a, b, c), - ForeignItemKind::Fn(a, b, c, d) => ItemKind::Fn(a, b, c, d), - ForeignItemKind::TyAlias(a, b, c, d) => ItemKind::TyAlias(a, b, c, d), + ForeignItemKind::Fn(fn_kind) => ItemKind::Fn(fn_kind), + ForeignItemKind::TyAlias(ty_alias_kind) => ItemKind::TyAlias(ty_alias_kind), ForeignItemKind::MacCall(a) => ItemKind::MacCall(a), } } @@ -2902,8 +2900,8 @@ impl TryFrom for ForeignItemKind { fn try_from(item_kind: ItemKind) -> Result { Ok(match item_kind { ItemKind::Static(a, b, c) => ForeignItemKind::Static(a, b, c), - ItemKind::Fn(a, b, c, d) => ForeignItemKind::Fn(a, b, c, d), - ItemKind::TyAlias(a, b, c, d) => ForeignItemKind::TyAlias(a, b, c, d), + ItemKind::Fn(fn_kind) => ForeignItemKind::Fn(fn_kind), + ItemKind::TyAlias(ty_alias_kind) => ForeignItemKind::TyAlias(ty_alias_kind), ItemKind::MacCall(a) => ForeignItemKind::MacCall(a), _ => return Err(item_kind), }) @@ -2911,3 +2909,69 @@ impl TryFrom for ForeignItemKind { } pub type ForeignItem = Item; + +pub trait HasTokens { + /// Called by `Parser::collect_tokens` to store the collected + /// tokens inside an AST node + fn finalize_tokens(&mut self, tokens: LazyTokenStream); +} + +impl HasTokens for P { + fn finalize_tokens(&mut self, tokens: LazyTokenStream) { + (**self).finalize_tokens(tokens); + } +} + +impl HasTokens for Option { + fn finalize_tokens(&mut self, tokens: LazyTokenStream) { + if let Some(inner) = self { + inner.finalize_tokens(tokens); + } + } +} + +impl HasTokens for Attribute { + fn finalize_tokens(&mut self, tokens: LazyTokenStream) { + match &mut self.kind { + AttrKind::Normal(_, attr_tokens) => { + if attr_tokens.is_none() { + *attr_tokens = Some(tokens); + } + } + AttrKind::DocComment(..) => { + panic!("Called finalize_tokens on doc comment attr {:?}", self) + } + } + } +} + +impl HasTokens for Stmt { + fn finalize_tokens(&mut self, tokens: LazyTokenStream) { + let stmt_tokens = match self.kind { + StmtKind::Local(ref mut local) => &mut local.tokens, + StmtKind::Item(ref mut item) => &mut item.tokens, + StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => &mut expr.tokens, + StmtKind::Empty => return, + StmtKind::MacCall(ref mut mac) => &mut mac.tokens, + }; + if stmt_tokens.is_none() { + *stmt_tokens = Some(tokens); + } + } +} + +macro_rules! derive_has_tokens { + ($($ty:path),*) => { $( + impl HasTokens for $ty { + fn finalize_tokens(&mut self, tokens: LazyTokenStream) { + if self.tokens.is_none() { + self.tokens = Some(tokens); + } + } + } + )* } +} + +derive_has_tokens! { + Item, Expr, Ty, AttrItem, Visibility, Path, Block, Pat +} diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 19c7c479f0..4dcbe4831b 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -234,10 +234,7 @@ impl MetaItem { } pub fn is_word(&self) -> bool { - match self.kind { - MetaItemKind::Word => true, - _ => false, - } + matches!(self.kind, MetaItemKind::Word) } pub fn has_name(&self, name: Symbol) -> bool { @@ -479,7 +476,7 @@ impl MetaItemKind { pub fn mac_args(&self, span: Span) -> MacArgs { match self { MetaItemKind::Word => MacArgs::Empty, - MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.token_tree().into()), + MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.to_token()), MetaItemKind::List(list) => { let mut tts = Vec::new(); for (i, item) in list.iter().enumerate() { @@ -501,7 +498,10 @@ impl MetaItemKind { match *self { MetaItemKind::Word => vec![], MetaItemKind::NameValue(ref lit) => { - vec![TokenTree::token(token::Eq, span).into(), lit.token_tree().into()] + vec![ + TokenTree::token(token::Eq, span).into(), + TokenTree::Token(lit.to_token()).into(), + ] } MetaItemKind::List(ref list) => { let mut tokens = Vec::new(); @@ -526,7 +526,7 @@ impl MetaItemKind { fn list_from_tokens(tokens: TokenStream) -> Option { let mut tokens = tokens.into_trees().peekable(); let mut result = Vec::new(); - while let Some(..) = tokens.peek() { + while tokens.peek().is_some() { let item = NestedMetaItem::from_tokens(&mut tokens)?; result.push(item); match tokens.next() { @@ -557,10 +557,7 @@ impl MetaItemKind { MetaItemKind::list_from_tokens(tokens.clone()) } MacArgs::Delimited(..) => None, - MacArgs::Eq(_, tokens) => { - assert!(tokens.len() == 1); - MetaItemKind::name_value_from_tokens(&mut tokens.trees()) - } + MacArgs::Eq(_, token) => Lit::from_token(token).ok().map(MetaItemKind::NameValue), MacArgs::Empty => Some(MetaItemKind::Word), } } @@ -595,7 +592,7 @@ impl NestedMetaItem { fn token_trees_and_spacings(&self) -> Vec { match *self { NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(), - NestedMetaItem::Literal(ref lit) => vec![lit.token_tree().into()], + NestedMetaItem::Literal(ref lit) => vec![TokenTree::Token(lit.to_token()).into()], } } diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 8a20dd7968..ddf52caed0 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -9,6 +9,7 @@ test(attr(deny(warnings))) )] #![feature(box_syntax)] +#![feature(box_patterns)] #![feature(const_fn)] // For the `transmute` in `P::new` #![feature(const_fn_transmute)] #![feature(const_panic)] diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 3889ede7f4..024d9687f3 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -28,7 +28,7 @@ pub trait ExpectOne { impl ExpectOne for SmallVec { fn expect_one(self, err: &'static str) -> A::Item { - assert!(self.len() == 1, err); + assert!(self.len() == 1, "{}", err); self.into_iter().next().unwrap() } } @@ -365,18 +365,16 @@ pub fn visit_mac_args(args: &mut MacArgs, vis: &mut T) { visit_delim_span(dspan, vis); visit_tts(tokens, vis); } - MacArgs::Eq(eq_span, tokens) => { + MacArgs::Eq(eq_span, token) => { vis.visit_span(eq_span); - visit_tts(tokens, vis); - // The value in `#[key = VALUE]` must be visited as an expression for backward - // compatibility, so that macros can be expanded in that position. - if !vis.token_visiting_enabled() { - match Lrc::make_mut(&mut tokens.0).get_mut(0) { - Some((TokenTree::Token(token), _spacing)) => match &mut token.kind { - token::Interpolated(nt) => match Lrc::make_mut(nt) { - token::NtExpr(expr) => vis.visit_expr(expr), - t => panic!("unexpected token in key-value attribute: {:?}", t), - }, + if vis.token_visiting_enabled() { + visit_token(token, vis); + } else { + // The value in `#[key = VALUE]` must be visited as an expression for backward + // compatibility, so that macros can be expanded in that position. + match &mut token.kind { + token::Interpolated(nt) => match Lrc::make_mut(nt) { + token::NtExpr(expr) => vis.visit_expr(expr), t => panic!("unexpected token in key-value attribute: {:?}", t), }, t => panic!("unexpected token in key-value attribute: {:?}", t), @@ -567,7 +565,7 @@ pub fn noop_visit_parenthesized_parameter_data( args: &mut ParenthesizedArgs, vis: &mut T, ) { - let ParenthesizedArgs { inputs, output, span } = args; + let ParenthesizedArgs { inputs, output, span, .. } = args; visit_vec(inputs, |input| vis.visit_ty(input)); noop_visit_fn_ret_ty(output, vis); vis.visit_span(span); @@ -790,8 +788,9 @@ pub fn noop_flat_map_generic_param( GenericParamKind::Type { default } => { visit_opt(default, |default| vis.visit_ty(default)); } - GenericParamKind::Const { ty, kw_span: _ } => { + GenericParamKind::Const { ty, kw_span: _, default } => { vis.visit_ty(ty); + visit_opt(default, |default| vis.visit_anon_const(default)); } } smallvec![param] @@ -913,7 +912,7 @@ pub fn noop_visit_item_kind(kind: &mut ItemKind, vis: &mut T) { vis.visit_ty(ty); visit_opt(expr, |expr| vis.visit_expr(expr)); } - ItemKind::Fn(_, sig, generics, body) => { + ItemKind::Fn(box FnKind(_, sig, generics, body)) => { visit_fn_sig(sig, vis); vis.visit_generics(generics); visit_opt(body, |body| vis.visit_block(body)); @@ -921,7 +920,7 @@ pub fn noop_visit_item_kind(kind: &mut ItemKind, vis: &mut T) { ItemKind::Mod(m) => vis.visit_mod(m), ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm), ItemKind::GlobalAsm(_ga) => {} - ItemKind::TyAlias(_, generics, bounds, ty) => { + ItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => { vis.visit_generics(generics); visit_bounds(bounds, vis); visit_opt(ty, |ty| vis.visit_ty(ty)); @@ -934,7 +933,7 @@ pub fn noop_visit_item_kind(kind: &mut ItemKind, vis: &mut T) { vis.visit_variant_data(variant_data); vis.visit_generics(generics); } - ItemKind::Impl { + ItemKind::Impl(box ImplKind { unsafety: _, polarity: _, defaultness: _, @@ -943,13 +942,13 @@ pub fn noop_visit_item_kind(kind: &mut ItemKind, vis: &mut T) { of_trait, self_ty, items, - } => { + }) => { vis.visit_generics(generics); visit_opt(of_trait, |trait_ref| vis.visit_trait_ref(trait_ref)); vis.visit_ty(self_ty); items.flat_map_in_place(|item| vis.flat_map_impl_item(item)); } - ItemKind::Trait(_is_auto, _unsafety, generics, bounds, items) => { + ItemKind::Trait(box TraitKind(.., generics, bounds, items)) => { vis.visit_generics(generics); visit_bounds(bounds, vis); items.flat_map_in_place(|item| vis.flat_map_trait_item(item)); @@ -977,12 +976,12 @@ pub fn noop_flat_map_assoc_item( visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } - AssocItemKind::Fn(_, sig, generics, body) => { + AssocItemKind::Fn(box FnKind(_, sig, generics, body)) => { visitor.visit_generics(generics); visit_fn_sig(sig, visitor); visit_opt(body, |body| visitor.visit_block(body)); } - AssocItemKind::TyAlias(_, generics, bounds, ty) => { + AssocItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => { visitor.visit_generics(generics); visit_bounds(bounds, visitor); visit_opt(ty, |ty| visitor.visit_ty(ty)); @@ -1067,12 +1066,12 @@ pub fn noop_flat_map_foreign_item( visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } - ForeignItemKind::Fn(_, sig, generics, body) => { + ForeignItemKind::Fn(box FnKind(_, sig, generics, body)) => { visitor.visit_generics(generics); visit_fn_sig(sig, visitor); visit_opt(body, |body| visitor.visit_block(body)); } - ForeignItemKind::TyAlias(_, generics, bounds, ty) => { + ForeignItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => { visitor.visit_generics(generics); visit_bounds(bounds, visitor); visit_opt(ty, |ty| visitor.visit_ty(ty)); diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index a74464937c..90bfb01d6c 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -15,7 +15,7 @@ use rustc_span::hygiene::ExpnKind; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym}; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{self, FileName, RealFileName, Span, DUMMY_SP}; +use rustc_span::{self, edition::Edition, FileName, RealFileName, Span, DUMMY_SP}; use std::borrow::Cow; use std::{fmt, mem}; @@ -130,10 +130,7 @@ impl LitKind { } crate fn may_have_suffix(self) -> bool { - match self { - Integer | Float | Err => true, - _ => false, - } + matches!(self, Integer | Float | Err) } } @@ -305,10 +302,7 @@ impl TokenKind { } pub fn should_end_const_arg(&self) -> bool { - match self { - Gt | Ge | BinOp(Shr) | BinOpEq(Shr) => true, - _ => false, - } + matches!(self, Gt | Ge | BinOp(Shr) | BinOpEq(Shr)) } } @@ -346,18 +340,21 @@ impl Token { } pub fn is_op(&self) -> bool { - match self.kind { - OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..) - | Lifetime(..) | Interpolated(..) | Eof => false, - _ => true, - } + !matches!( + self.kind, + OpenDelim(..) + | CloseDelim(..) + | Literal(..) + | DocComment(..) + | Ident(..) + | Lifetime(..) + | Interpolated(..) + | Eof + ) } pub fn is_like_plus(&self) -> bool { - match self.kind { - BinOp(Plus) | BinOpEq(Plus) => true, - _ => false, - } + matches!(self.kind, BinOp(Plus) | BinOpEq(Plus)) } /// Returns `true` if the token can appear at the start of an expression. @@ -379,13 +376,10 @@ impl Token { ModSep | // global path Lifetime(..) | // labeled loop Pound => true, // expression attributes - Interpolated(ref nt) => match **nt { - NtLiteral(..) | + Interpolated(ref nt) => matches!(**nt, NtLiteral(..) | NtExpr(..) | NtBlock(..) | - NtPath(..) => true, - _ => false, - }, + NtPath(..)), _ => false, } } @@ -405,10 +399,7 @@ impl Token { Lifetime(..) | // lifetime bound in trait object Lt | BinOp(Shl) | // associated path ModSep => true, // global path - Interpolated(ref nt) => match **nt { - NtTy(..) | NtPath(..) => true, - _ => false, - }, + Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)), _ => false, } } @@ -417,10 +408,7 @@ impl Token { pub fn can_begin_const_arg(&self) -> bool { match self.kind { OpenDelim(Brace) => true, - Interpolated(ref nt) => match **nt { - NtExpr(..) | NtBlock(..) | NtLiteral(..) => true, - _ => false, - }, + Interpolated(ref nt) => matches!(**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)), _ => self.can_begin_literal_maybe_minus(), } } @@ -436,10 +424,7 @@ impl Token { /// Returns `true` if the token is any literal. pub fn is_lit(&self) -> bool { - match self.kind { - Literal(..) => true, - _ => false, - } + matches!(self.kind, Literal(..)) } /// Returns `true` if the token is any literal, a minus (which can prefix a literal, @@ -705,7 +690,16 @@ pub enum NonterminalKind { Item, Block, Stmt, - Pat, + Pat2018 { + /// Keep track of whether the user used `:pat2018` or `:pat` and we inferred it from the + /// edition of the span. This is used for diagnostics. + inferred: bool, + }, + Pat2021 { + /// Keep track of whether the user used `:pat2018` or `:pat` and we inferred it from the + /// edition of the span. This is used for diagnostics. + inferred: bool, + }, Expr, Ty, Ident, @@ -718,12 +712,24 @@ pub enum NonterminalKind { } impl NonterminalKind { - pub fn from_symbol(symbol: Symbol) -> Option { + /// The `edition` closure is used to get the edition for the given symbol. Doing + /// `span.edition()` is expensive, so we do it lazily. + pub fn from_symbol( + symbol: Symbol, + edition: impl FnOnce() -> Edition, + ) -> Option { Some(match symbol { sym::item => NonterminalKind::Item, sym::block => NonterminalKind::Block, sym::stmt => NonterminalKind::Stmt, - sym::pat => NonterminalKind::Pat, + sym::pat => match edition() { + Edition::Edition2015 | Edition::Edition2018 => { + NonterminalKind::Pat2018 { inferred: true } + } + Edition::Edition2021 => NonterminalKind::Pat2021 { inferred: true }, + }, + sym::pat2018 => NonterminalKind::Pat2018 { inferred: false }, + sym::pat2021 => NonterminalKind::Pat2021 { inferred: false }, sym::expr => NonterminalKind::Expr, sym::ty => NonterminalKind::Ty, sym::ident => NonterminalKind::Ident, @@ -741,7 +747,10 @@ impl NonterminalKind { NonterminalKind::Item => sym::item, NonterminalKind::Block => sym::block, NonterminalKind::Stmt => sym::stmt, - NonterminalKind::Pat => sym::pat, + NonterminalKind::Pat2018 { inferred: false } => sym::pat2018, + NonterminalKind::Pat2021 { inferred: false } => sym::pat2021, + NonterminalKind::Pat2018 { inferred: true } + | NonterminalKind::Pat2021 { inferred: true } => sym::pat, NonterminalKind::Expr => sym::expr, NonterminalKind::Ty => sym::ty, NonterminalKind::Ident => sym::ident, @@ -762,7 +771,7 @@ impl fmt::Display for NonterminalKind { } impl Nonterminal { - fn span(&self) -> Span { + pub fn span(&self) -> Span { match self { NtItem(item) => item.span, NtBlock(block) => block.span, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index b2207f2281..9ac05f316f 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -1,15 +1,15 @@ //! # Token Streams //! //! `TokenStream`s represent syntactic objects before they are converted into ASTs. -//! A `TokenStream` is, roughly speaking, a sequence (eg stream) of `TokenTree`s, -//! which are themselves a single `Token` or a `Delimited` subsequence of tokens. +//! A `TokenStream` is, roughly speaking, a sequence of [`TokenTree`]s, +//! which are themselves a single [`Token`] or a `Delimited` subsequence of tokens. //! //! ## Ownership //! //! `TokenStream`s are persistent data structures constructed as ropes with reference //! counted-children. In general, this means that calling an operation on a `TokenStream` //! (such as `slice`) produces an entirely new `TokenStream` from the borrowed reference to -//! the original. This essentially coerces `TokenStream`s into 'views' of their subparts, +//! the original. This essentially coerces `TokenStream`s into "views" of their subparts, //! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking //! ownership of the original. @@ -24,9 +24,9 @@ use smallvec::{smallvec, SmallVec}; use std::{fmt, iter, mem}; -/// When the main rust parser encounters a syntax-extension invocation, it -/// parses the arguments to the invocation as a token-tree. This is a very -/// loose structure, such that all sorts of different AST-fragments can +/// When the main Rust parser encounters a syntax-extension invocation, it +/// parses the arguments to the invocation as a token tree. This is a very +/// loose structure, such that all sorts of different AST fragments can /// be passed to syntax extensions using a uniform type. /// /// If the syntax extension is an MBE macro, it will attempt to match its @@ -38,12 +38,18 @@ use std::{fmt, iter, mem}; /// Nothing special happens to misnamed or misplaced `SubstNt`s. #[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] pub enum TokenTree { - /// A single token + /// A single token. Token(Token), - /// A delimited sequence of token trees + /// A delimited sequence of token trees. Delimited(DelimSpan, DelimToken, TokenStream), } +#[derive(Copy, Clone)] +pub enum CanSynthesizeMissingTokens { + Yes, + No, +} + // Ensure all fields of `TokenTree` is `Send` and `Sync`. #[cfg(parallel_compiler)] fn _dummy() @@ -56,7 +62,7 @@ where } impl TokenTree { - /// Checks if this TokenTree is equal to the other, regardless of span information. + /// Checks if this `TokenTree` is equal to the other, regardless of span information. pub fn eq_unspanned(&self, other: &TokenTree) -> bool { match (self, other) { (TokenTree::Token(token), TokenTree::Token(token2)) => token.kind == token2.kind, @@ -67,7 +73,7 @@ impl TokenTree { } } - /// Retrieves the TokenTree's span. + /// Retrieves the `TokenTree`'s span. pub fn span(&self) -> Span { match self { TokenTree::Token(token) => token.span, @@ -121,20 +127,16 @@ where } pub trait CreateTokenStream: sync::Send + sync::Sync { - fn add_trailing_semi(&self) -> Box; fn create_token_stream(&self) -> TokenStream; } impl CreateTokenStream for TokenStream { - fn add_trailing_semi(&self) -> Box { - panic!("Cannot call `add_trailing_semi` on a `TokenStream`!"); - } fn create_token_stream(&self) -> TokenStream { self.clone() } } -/// A lazy version of `TokenStream`, which defers creation +/// A lazy version of [`TokenStream`], which defers creation /// of an actual `TokenStream` until it is needed. /// `Box` is here only to reduce the structure size. #[derive(Clone)] @@ -145,13 +147,6 @@ impl LazyTokenStream { LazyTokenStream(Lrc::new(Box::new(inner))) } - /// Extends the captured stream by one token, - /// which must be a trailing semicolon. This - /// affects the `TokenStream` created by `make_tokenstream`. - pub fn add_trailing_semi(&self) -> LazyTokenStream { - LazyTokenStream(Lrc::new(self.0.add_trailing_semi())) - } - pub fn create_token_stream(&self) -> TokenStream { self.0.create_token_stream() } @@ -182,11 +177,12 @@ impl HashStable for LazyTokenStream { } } -/// A `TokenStream` is an abstract sequence of tokens, organized into `TokenTree`s. +/// A `TokenStream` is an abstract sequence of tokens, organized into [`TokenTree`]s. /// /// The goal is for procedural macros to work with `TokenStream`s and `TokenTree`s /// instead of a representation of the abstract syntax tree. -/// Today's `TokenTree`s can still contain AST via `token::Interpolated` for back-compat. +/// Today's `TokenTree`s can still contain AST via `token::Interpolated` for +/// backwards compatability. #[derive(Clone, Debug, Default, Encodable, Decodable)] pub struct TokenStream(pub(crate) Lrc>); @@ -423,7 +419,7 @@ impl TokenStreamBuilder { } } -/// By-reference iterator over a `TokenStream`. +/// By-reference iterator over a [`TokenStream`]. #[derive(Clone)] pub struct CursorRef<'t> { stream: &'t TokenStream, @@ -451,8 +447,8 @@ impl<'t> Iterator for CursorRef<'t> { } } -/// Owning by-value iterator over a `TokenStream`. -/// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones. +/// Owning by-value iterator over a [`TokenStream`]. +// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones. #[derive(Clone)] pub struct Cursor { pub stream: TokenStream, diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 60422a2e57..90786520fe 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -12,14 +12,14 @@ use crate::ast; /// |x| 5 /// isn't parsed as (if true {...} else {...} | x) | 5 pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { - match e.kind { + !matches!( + e.kind, ast::ExprKind::If(..) - | ast::ExprKind::Match(..) - | ast::ExprKind::Block(..) - | ast::ExprKind::While(..) - | ast::ExprKind::Loop(..) - | ast::ExprKind::ForLoop(..) - | ast::ExprKind::TryBlock(..) => false, - _ => true, - } + | ast::ExprKind::Match(..) + | ast::ExprKind::Block(..) + | ast::ExprKind::While(..) + | ast::ExprKind::Loop(..) + | ast::ExprKind::ForLoop(..) + | ast::ExprKind::TryBlock(..) + ) } diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs index 5d994c9037..542a330a03 100644 --- a/compiler/rustc_ast/src/util/comments.rs +++ b/compiler/rustc_ast/src/util/comments.rs @@ -180,10 +180,8 @@ pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec { if doc_style.is_none() { - let code_to_the_right = match text[pos + token.len..].chars().next() { - Some('\r' | '\n') => false, - _ => true, - }; + let code_to_the_right = + !matches!(text[pos + token.len..].chars().next(), Some('\r' | '\n')); let style = match (code_to_the_left, code_to_the_right) { (_, true) => CommentStyle::Mixed, (false, false) => CommentStyle::Isolated, diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index f6f1ad0a9c..2124f1efb9 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -2,7 +2,6 @@ use crate::ast::{self, Lit, LitKind}; use crate::token::{self, Token}; -use crate::tokenstream::TokenTree; use rustc_lexer::unescape::{unescape_byte, unescape_char}; use rustc_lexer::unescape::{unescape_byte_literal, unescape_literal, Mode}; @@ -88,7 +87,6 @@ impl LitKind { } }); error?; - buf.shrink_to_fit(); Symbol::intern(&buf) } else { symbol @@ -106,7 +104,6 @@ impl LitKind { } }); error?; - buf.shrink_to_fit(); LitKind::ByteStr(buf.into()) } token::ByteStrRaw(_) => { @@ -121,7 +118,6 @@ impl LitKind { } }); error?; - buf.shrink_to_fit(); buf } else { symbol.to_string().into_bytes() @@ -225,13 +221,13 @@ impl Lit { Lit { token: kind.to_lit_token(), kind, span } } - /// Losslessly convert an AST literal into a token stream. - pub fn token_tree(&self) -> TokenTree { - let token = match self.token.kind { + /// Losslessly convert an AST literal into a token. + pub fn to_token(&self) -> Token { + let kind = match self.token.kind { token::Bool => token::Ident(self.token.symbol, false), _ => token::Literal(self.token), }; - TokenTree::token(token, self.span) + Token::new(kind, self.span) } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index a420bb5635..c37d4cd9f7 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -15,7 +15,6 @@ use crate::ast::*; use crate::token; -use crate::tokenstream::TokenTree; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -293,7 +292,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_ty(typ); walk_list!(visitor, visit_expr, expr); } - ItemKind::Fn(_, ref sig, ref generics, ref body) => { + ItemKind::Fn(box FnKind(_, ref sig, ref generics, ref body)) => { visitor.visit_generics(generics); let kind = FnKind::Fn(FnCtxt::Free, item.ident, sig, &item.vis, body.as_deref()); visitor.visit_fn(kind, item.span, item.id) @@ -303,7 +302,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { walk_list!(visitor, visit_foreign_item, &foreign_module.items); } ItemKind::GlobalAsm(ref ga) => visitor.visit_global_asm(ga), - ItemKind::TyAlias(_, ref generics, ref bounds, ref ty) => { + ItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref ty)) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_ty, ty); @@ -312,7 +311,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_generics(generics); visitor.visit_enum_def(enum_definition, generics, item.id, item.span) } - ItemKind::Impl { + ItemKind::Impl(box ImplKind { unsafety: _, polarity: _, defaultness: _, @@ -321,7 +320,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { ref of_trait, ref self_ty, ref items, - } => { + }) => { visitor.visit_generics(generics); walk_list!(visitor, visit_trait_ref, of_trait); visitor.visit_ty(self_ty); @@ -332,7 +331,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_generics(generics); visitor.visit_variant_data(struct_definition); } - ItemKind::Trait(.., ref generics, ref bounds, ref items) => { + ItemKind::Trait(box TraitKind(.., ref generics, ref bounds, ref items)) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait); @@ -544,12 +543,12 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignI visitor.visit_ty(ty); walk_list!(visitor, visit_expr, expr); } - ForeignItemKind::Fn(_, sig, generics, body) => { + ForeignItemKind::Fn(box FnKind(_, sig, generics, body)) => { visitor.visit_generics(generics); let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, body.as_deref()); visitor.visit_fn(kind, span, id); } - ForeignItemKind::TyAlias(_, generics, bounds, ty) => { + ForeignItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_ty, ty); @@ -578,7 +577,12 @@ pub fn walk_generic_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Generi match param.kind { GenericParamKind::Lifetime => (), GenericParamKind::Type { ref default } => walk_list!(visitor, visit_ty, default), - GenericParamKind::Const { ref ty, .. } => visitor.visit_ty(ty), + GenericParamKind::Const { ref ty, ref default, .. } => { + visitor.visit_ty(ty); + if let Some(default) = default { + visitor.visit_anon_const(default); + } + } } } @@ -649,12 +653,12 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem, visitor.visit_ty(ty); walk_list!(visitor, visit_expr, expr); } - AssocItemKind::Fn(_, sig, generics, body) => { + AssocItemKind::Fn(box FnKind(_, sig, generics, body)) => { visitor.visit_generics(generics); let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, body.as_deref()); visitor.visit_fn(kind, span, id); } - AssocItemKind::TyAlias(_, generics, bounds, ty) => { + AssocItemKind::TyAlias(box TyAliasKind(_, generics, bounds, ty)) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_ty, ty); @@ -900,12 +904,9 @@ pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) { MacArgs::Delimited(_dspan, _delim, _tokens) => {} // The value in `#[key = VALUE]` must be visited as an expression for backward // compatibility, so that macros can be expanded in that position. - MacArgs::Eq(_eq_span, tokens) => match tokens.trees_ref().next() { - Some(TokenTree::Token(token)) => match &token.kind { - token::Interpolated(nt) => match &**nt { - token::NtExpr(expr) => visitor.visit_expr(expr), - t => panic!("unexpected token in key-value attribute: {:?}", t), - }, + MacArgs::Eq(_eq_span, token) => match &token.kind { + token::Interpolated(nt) => match &**nt { + token::NtExpr(expr) => visitor.visit_expr(expr), t => panic!("unexpected token in key-value attribute: {:?}", t), }, t => panic!("unexpected token in key-value attribute: {:?}", t), diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 9b1642df11..4d6afd2fe0 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -10,9 +10,9 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_session::parse::feature_err; -use rustc_span::hygiene::ForLoopLoc; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::{hygiene::ForLoopLoc, DUMMY_SP}; use rustc_target::asm; use std::collections::hash_map::Entry; use std::fmt::Write; @@ -87,9 +87,12 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Let(ref pat, ref scrutinee) => { self.lower_expr_let(e.span, pat, scrutinee) } - ExprKind::If(ref cond, ref then, ref else_opt) => { - self.lower_expr_if(e.span, cond, then, else_opt.as_deref()) - } + ExprKind::If(ref cond, ref then, ref else_opt) => match cond.kind { + ExprKind::Let(ref pat, ref scrutinee) => { + self.lower_expr_if_let(e.span, pat, scrutinee, then, else_opt.as_deref()) + } + _ => self.lower_expr_if(cond, then, else_opt.as_deref()), + }, ExprKind::While(ref cond, ref body, opt_label) => self .with_loop_scope(e.id, |this| { this.lower_expr_while_in_loop_scope(e.span, cond, body, opt_label) @@ -99,6 +102,7 @@ impl<'hir> LoweringContext<'_, 'hir> { this.lower_block(body, false), opt_label, hir::LoopSource::Loop, + DUMMY_SP, ) }), ExprKind::TryBlock(ref body) => self.lower_expr_try_block(body), @@ -337,10 +341,30 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_if( &mut self, - span: Span, cond: &Expr, then: &Block, else_opt: Option<&Expr>, + ) -> hir::ExprKind<'hir> { + macro_rules! make_if { + ($opt:expr) => {{ + let then_expr = self.lower_block_expr(then); + hir::ExprKind::If(self.lower_expr(cond), self.arena.alloc(then_expr), $opt) + }}; + } + if let Some(rslt) = else_opt { + make_if!(Some(self.lower_expr(rslt))) + } else { + make_if!(None) + } + } + + fn lower_expr_if_let( + &mut self, + span: Span, + pat: &Pat, + scrutinee: &Expr, + then: &Block, + else_opt: Option<&Expr>, ) -> hir::ExprKind<'hir> { // FIXME(#53667): handle lowering of && and parens. @@ -353,30 +377,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let else_arm = self.arm(else_pat, else_expr); // Handle then + scrutinee: - let (then_pat, scrutinee, desugar) = match cond.kind { - // ` => `: - ExprKind::Let(ref pat, ref scrutinee) => { - let scrutinee = self.lower_expr(scrutinee); - let pat = self.lower_pat(pat); - (pat, scrutinee, hir::MatchSource::IfLetDesugar { contains_else_clause }) - } - // `true => `: - _ => { - // Lower condition: - let cond = self.lower_expr(cond); - let span_block = - self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None); - // Wrap in a construct equivalent to `{ let _t = $cond; _t }` - // to preserve drop semantics since `if cond { ... }` does not - // let temporaries live outside of `cond`. - let cond = self.expr_drop_temps(span_block, cond, ThinVec::new()); - let pat = self.pat_bool(span, true); - (pat, cond, hir::MatchSource::IfDesugar { contains_else_clause }) - } - }; + let scrutinee = self.lower_expr(scrutinee); + let then_pat = self.lower_pat(pat); + let then_expr = self.lower_block_expr(then); let then_arm = self.arm(then_pat, self.arena.alloc(then_expr)); + let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause }; hir::ExprKind::Match(scrutinee, arena_vec![self; then_arm, else_arm], desugar) } @@ -447,7 +454,12 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr_match(span, scrutinee, arena_vec![self; then_arm, else_arm], desugar); // `[opt_ident]: loop { ... }` - hir::ExprKind::Loop(self.block_expr(self.arena.alloc(match_expr)), opt_label, source) + hir::ExprKind::Loop( + self.block_expr(self.arena.alloc(match_expr)), + opt_label, + source, + span.with_hi(cond.span.hi()), + ) } /// Desugar `try { ; }` into `{ ; ::std::ops::Try::from_ok() }`, @@ -742,7 +754,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // loop { .. } let loop_expr = self.arena.alloc(hir::Expr { hir_id: loop_hir_id, - kind: hir::ExprKind::Loop(loop_block, None, hir::LoopSource::Loop), + kind: hir::ExprKind::Loop(loop_block, None, hir::LoopSource::Loop, span), span, attrs: ThinVec::new(), }); @@ -764,10 +776,7 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Expr, fn_decl_span: Span, ) -> hir::ExprKind<'hir> { - // Lower outside new scope to preserve `is_in_loop_condition`. - let fn_decl = self.lower_fn_decl(decl, None, false, None); - - self.with_new_scopes(move |this| { + let (body_id, generator_option) = self.with_new_scopes(move |this| { let prev = this.current_item; this.current_item = Some(fn_decl_span); let mut generator_kind = None; @@ -779,8 +788,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let generator_option = this.generator_movability_for_fn(&decl, fn_decl_span, generator_kind, movability); this.current_item = prev; - hir::ExprKind::Closure(capture_clause, fn_decl, body_id, fn_decl_span, generator_option) - }) + (body_id, generator_option) + }); + + // Lower outside new scope to preserve `is_in_loop_condition`. + let fn_decl = self.lower_fn_decl(decl, None, false, None); + + hir::ExprKind::Closure(capture_clause, fn_decl, body_id, fn_decl_span, generator_option) } fn generator_movability_for_fn( @@ -826,12 +840,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::ExprKind<'hir> { let outer_decl = FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) }; - // We need to lower the declaration outside the new scope, because we - // have to conserve the state of being inside a loop condition for the - // closure argument types. - let fn_decl = self.lower_fn_decl(&outer_decl, None, false, None); - self.with_new_scopes(move |this| { + let body_id = self.with_new_scopes(|this| { // FIXME(cramertj): allow `async` non-`move` closures with arguments. if capture_clause == CaptureBy::Ref && !decl.inputs.is_empty() { struct_span_err!( @@ -862,8 +872,15 @@ impl<'hir> LoweringContext<'_, 'hir> { ); this.expr(fn_decl_span, async_body, ThinVec::new()) }); - hir::ExprKind::Closure(capture_clause, fn_decl, body_id, fn_decl_span, None) - }) + body_id + }); + + // We need to lower the declaration outside the new scope, because we + // have to conserve the state of being inside a loop condition for the + // closure argument types. + let fn_decl = self.lower_fn_decl(&outer_decl, None, false, None); + + hir::ExprKind::Closure(capture_clause, fn_decl, body_id, fn_decl_span, None) } /// Destructure the LHS of complex assignments. @@ -1703,7 +1720,12 @@ impl<'hir> LoweringContext<'_, 'hir> { ); // `[opt_ident]: loop { ... }` - let kind = hir::ExprKind::Loop(loop_block, opt_label, hir::LoopSource::ForLoop); + let kind = hir::ExprKind::Loop( + loop_block, + opt_label, + hir::LoopSource::ForLoop, + e.span.with_hi(orig_head_span.hi()), + ); let loop_expr = self.arena.alloc(hir::Expr { hir_id: self.lower_node_id(e.id), kind, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index eef6d38aa0..1efe83cace 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -67,7 +67,7 @@ impl<'a> Visitor<'a> for ItemLowerer<'a, '_, '_> { if let Some(hir_id) = item_hir_id { self.lctx.with_parent_item_lifetime_defs(hir_id, |this| { let this = &mut ItemLowerer { lctx: this }; - if let ItemKind::Impl { ref of_trait, .. } = item.kind { + if let ItemKind::Impl(box ImplKind { ref of_trait, .. }) = item.kind { this.with_trait_impl_ref(of_trait, |this| visit::walk_item(this, item)); } else { visit::walk_item(this, item); @@ -134,7 +134,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let old_len = self.in_scope_lifetimes.len(); let parent_generics = match self.items.get(&parent_hir_id).unwrap().kind { - hir::ItemKind::Impl { ref generics, .. } + hir::ItemKind::Impl(hir::Impl { ref generics, .. }) | hir::ItemKind::Trait(_, _, ref generics, ..) => &generics.params[..], _ => &[], }; @@ -189,7 +189,9 @@ impl<'hir> LoweringContext<'_, 'hir> { vec } ItemKind::MacroDef(..) => SmallVec::new(), - ItemKind::Fn(..) | ItemKind::Impl { of_trait: None, .. } => smallvec![i.id], + ItemKind::Fn(..) | ItemKind::Impl(box ImplKind { of_trait: None, .. }) => { + smallvec![i.id] + } _ => smallvec![i.id], }; @@ -276,12 +278,12 @@ impl<'hir> LoweringContext<'_, 'hir> { let (ty, body_id) = self.lower_const_item(t, span, e.as_deref()); hir::ItemKind::Const(ty, body_id) } - ItemKind::Fn( + ItemKind::Fn(box FnKind( _, FnSig { ref decl, header, span: fn_sig_span }, ref generics, ref body, - ) => { + )) => { let fn_def_id = self.resolver.local_def_id(id); self.with_new_scopes(|this| { this.current_item = Some(ident.span); @@ -310,21 +312,26 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let sig = hir::FnSig { decl, - header: this.lower_fn_header(header), + header: this.lower_fn_header(header, fn_sig_span, id), span: fn_sig_span, }; hir::ItemKind::Fn(sig, generics, body_id) }) } ItemKind::Mod(ref m) => hir::ItemKind::Mod(self.lower_mod(m)), - ItemKind::ForeignMod(ref fm) => hir::ItemKind::ForeignMod { - abi: fm.abi.map_or(abi::Abi::C, |abi| self.lower_abi(abi)), - items: self - .arena - .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))), - }, + ItemKind::ForeignMod(ref fm) => { + if fm.abi.is_none() { + self.maybe_lint_missing_abi(span, id, abi::Abi::C); + } + hir::ItemKind::ForeignMod { + abi: fm.abi.map_or(abi::Abi::C, |abi| self.lower_abi(abi)), + items: self + .arena + .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))), + } + } ItemKind::GlobalAsm(ref ga) => hir::ItemKind::GlobalAsm(self.lower_global_asm(ga)), - ItemKind::TyAlias(_, ref gen, _, Some(ref ty)) => { + ItemKind::TyAlias(box TyAliasKind(_, ref gen, _, Some(ref ty))) => { // We lower // // type Foo = impl Trait @@ -343,7 +350,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let generics = self.lower_generics(gen, ImplTraitContext::disallowed()); hir::ItemKind::TyAlias(ty, generics) } - ItemKind::TyAlias(_, ref generics, _, None) => { + ItemKind::TyAlias(box TyAliasKind(_, ref generics, _, None)) => { let ty = self.arena.alloc(self.ty(span, hir::TyKind::Err)); let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); hir::ItemKind::TyAlias(ty, generics) @@ -370,7 +377,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_generics(generics, ImplTraitContext::disallowed()), ) } - ItemKind::Impl { + ItemKind::Impl(box ImplKind { unsafety, polarity, defaultness, @@ -379,7 +386,7 @@ impl<'hir> LoweringContext<'_, 'hir> { of_trait: ref trait_ref, self_ty: ref ty, items: ref impl_items, - } => { + }) => { let def_id = self.resolver.local_def_id(id); // Lower the "impl header" first. This ordering is important @@ -431,7 +438,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // to not cause an assertion failure inside the `lower_defaultness` function. let has_val = true; let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val); - hir::ItemKind::Impl { + hir::ItemKind::Impl(hir::Impl { unsafety: self.lower_unsafety(unsafety), polarity, defaultness, @@ -441,9 +448,15 @@ impl<'hir> LoweringContext<'_, 'hir> { of_trait: trait_ref, self_ty: lowered_ty, items: new_impl_items, - } + }) } - ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref items) => { + ItemKind::Trait(box TraitKind( + is_auto, + unsafety, + ref generics, + ref bounds, + ref items, + )) => { let bounds = self.lower_param_bounds(bounds, ImplTraitContext::disallowed()); let items = self .arena @@ -693,7 +706,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ident: i.ident, attrs: self.lower_attrs(&i.attrs), kind: match i.kind { - ForeignItemKind::Fn(_, ref sig, ref generics, _) => { + ForeignItemKind::Fn(box FnKind(_, ref sig, ref generics, _)) => { let fdec = &sig.decl; let (generics, (fn_dec, fn_args)) = self.add_in_band_defs( generics, @@ -798,19 +811,19 @@ impl<'hir> LoweringContext<'_, 'hir> { let body = default.as_ref().map(|x| self.lower_const_body(i.span, Some(x))); (hir::Generics::empty(), hir::TraitItemKind::Const(ty, body)) } - AssocItemKind::Fn(_, ref sig, ref generics, None) => { + AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, None)) => { let names = self.lower_fn_params_to_names(&sig.decl); let (generics, sig) = - self.lower_method_sig(generics, sig, trait_item_def_id, false, None); + self.lower_method_sig(generics, sig, trait_item_def_id, false, None, i.id); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names))) } - AssocItemKind::Fn(_, ref sig, ref generics, Some(ref body)) => { + AssocItemKind::Fn(box FnKind(_, ref sig, ref generics, Some(ref body))) => { let body_id = self.lower_fn_body_block(i.span, &sig.decl, Some(body)); let (generics, sig) = - self.lower_method_sig(generics, sig, trait_item_def_id, false, None); + self.lower_method_sig(generics, sig, trait_item_def_id, false, None, i.id); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id))) } - AssocItemKind::TyAlias(_, ref generics, ref bounds, ref default) => { + AssocItemKind::TyAlias(box TyAliasKind(_, ref generics, ref bounds, ref default)) => { let ty = default.as_ref().map(|x| self.lower_ty(x, ImplTraitContext::disallowed())); let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); let kind = hir::TraitItemKind::Type( @@ -836,10 +849,10 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_trait_item_ref(&mut self, i: &AssocItem) -> hir::TraitItemRef { let (kind, has_default) = match &i.kind { AssocItemKind::Const(_, _, default) => (hir::AssocItemKind::Const, default.is_some()), - AssocItemKind::TyAlias(_, _, _, default) => { + AssocItemKind::TyAlias(box TyAliasKind(_, _, _, default)) => { (hir::AssocItemKind::Type, default.is_some()) } - AssocItemKind::Fn(_, sig, _, default) => { + AssocItemKind::Fn(box FnKind(_, sig, _, default)) => { (hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }, default.is_some()) } AssocItemKind::MacCall(..) => unimplemented!(), @@ -865,7 +878,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ImplItemKind::Const(ty, self.lower_const_body(i.span, expr.as_deref())), ) } - AssocItemKind::Fn(_, sig, generics, body) => { + AssocItemKind::Fn(box FnKind(_, sig, generics, body)) => { self.current_item = Some(i.span); let asyncness = sig.header.asyncness; let body_id = @@ -877,11 +890,12 @@ impl<'hir> LoweringContext<'_, 'hir> { impl_item_def_id, impl_trait_return_allow, asyncness.opt_return_id(), + i.id, ); (generics, hir::ImplItemKind::Fn(sig, body_id)) } - AssocItemKind::TyAlias(_, generics, _, ty) => { + AssocItemKind::TyAlias(box TyAliasKind(_, generics, _, ty)) => { let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); let kind = match ty { None => { @@ -932,7 +946,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind: match &i.kind { AssocItemKind::Const(..) => hir::AssocItemKind::Const, AssocItemKind::TyAlias(..) => hir::AssocItemKind::Type, - AssocItemKind::Fn(_, sig, ..) => { + AssocItemKind::Fn(box FnKind(_, sig, ..)) => { hir::AssocItemKind::Fn { has_self: sig.decl.has_self() } } AssocItemKind::MacCall(..) => unimplemented!(), @@ -1270,8 +1284,9 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_def_id: LocalDefId, impl_trait_return_allow: bool, is_async: Option, + id: NodeId, ) -> (hir::Generics<'hir>, hir::FnSig<'hir>) { - let header = self.lower_fn_header(sig.header); + let header = self.lower_fn_header(sig.header, sig.span, id); let (generics, decl) = self.add_in_band_defs( generics, fn_def_id, @@ -1288,12 +1303,12 @@ impl<'hir> LoweringContext<'_, 'hir> { (generics, hir::FnSig { header, decl, span: sig.span }) } - fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader { + fn lower_fn_header(&mut self, h: FnHeader, span: Span, id: NodeId) -> hir::FnHeader { hir::FnHeader { unsafety: self.lower_unsafety(h.unsafety), asyncness: self.lower_asyncness(h.asyncness), constness: self.lower_constness(h.constness), - abi: self.lower_extern(h.ext), + abi: self.lower_extern(h.ext, span, id), } } @@ -1304,10 +1319,13 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - pub(super) fn lower_extern(&mut self, ext: Extern) -> abi::Abi { + pub(super) fn lower_extern(&mut self, ext: Extern, span: Span, id: NodeId) -> abi::Abi { match ext { Extern::None => abi::Abi::Rust, - Extern::Implicit => abi::Abi::C, + Extern::Implicit => { + self.maybe_lint_missing_abi(span, id, abi::Abi::C); + abi::Abi::C + } Extern::Explicit(abi) => self.lower_abi(abi), } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 2e1b5a74a7..f076dca5cf 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -30,14 +30,14 @@ //! get confused if the spans from leaf AST nodes occur in multiple places //! in the HIR, especially for multiple identifiers. -#![feature(array_value_iter)] #![feature(crate_visibility_modifier)] #![feature(or_patterns)] +#![feature(box_patterns)] #![recursion_limit = "256"] use rustc_ast::node_id::NodeMap; use rustc_ast::token::{self, DelimToken, Nonterminal, Token}; -use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, DelimSpan, TokenStream, TokenTree}; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::walk_list; use rustc_ast::{self as ast, *}; @@ -53,13 +53,15 @@ use rustc_hir::definitions::{DefKey, DefPathData, Definitions}; use rustc_hir::intravisit; use rustc_hir::{ConstArg, GenericArg, ParamName}; use rustc_index::vec::{Idx, IndexVec}; -use rustc_session::lint::{builtin::BARE_TRAIT_OBJECTS, BuiltinLintDiagnostics, LintBuffer}; +use rustc_session::lint::builtin::{BARE_TRAIT_OBJECTS, MISSING_ABI}; +use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::parse::ParseSess; use rustc_session::Session; use rustc_span::hygiene::ExpnId; -use rustc_span::source_map::{respan, DesugaringKind, ExpnData, ExpnKind}; +use rustc_span::source_map::{respan, DesugaringKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; +use rustc_target::spec::abi::Abi; use smallvec::{smallvec, SmallVec}; use std::collections::BTreeMap; @@ -206,7 +208,7 @@ pub trait ResolverAstLowering { ) -> LocalDefId; } -type NtToTokenstream = fn(&Nonterminal, &ParseSess, Span) -> TokenStream; +type NtToTokenstream = fn(&Nonterminal, &ParseSess, CanSynthesizeMissingTokens) -> TokenStream; /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree, /// and if so, what meaning it has. @@ -393,6 +395,42 @@ enum AnonymousLifetimeMode { PassThrough, } +struct TokenStreamLowering<'a> { + parse_sess: &'a ParseSess, + synthesize_tokens: CanSynthesizeMissingTokens, + nt_to_tokenstream: NtToTokenstream, +} + +impl<'a> TokenStreamLowering<'a> { + fn lower_token_stream(&mut self, tokens: TokenStream) -> TokenStream { + tokens.into_trees().flat_map(|tree| self.lower_token_tree(tree).into_trees()).collect() + } + + fn lower_token_tree(&mut self, tree: TokenTree) -> TokenStream { + match tree { + TokenTree::Token(token) => self.lower_token(token), + TokenTree::Delimited(span, delim, tts) => { + TokenTree::Delimited(span, delim, self.lower_token_stream(tts)).into() + } + } + } + + fn lower_token(&mut self, token: Token) -> TokenStream { + match token.kind { + token::Interpolated(nt) => { + let tts = (self.nt_to_tokenstream)(&nt, self.parse_sess, self.synthesize_tokens); + TokenTree::Delimited( + DelimSpan::from_single(token.span), + DelimToken::NoDelim, + self.lower_token_stream(tts), + ) + .into() + } + _ => TokenTree::Token(token).into(), + } + } +} + struct ImplTraitTypeIdVisitor<'a> { ids: &'a mut SmallVec<[NodeId; 1]>, } @@ -463,13 +501,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ItemKind::Struct(_, ref generics) | ItemKind::Union(_, ref generics) | ItemKind::Enum(_, ref generics) - | ItemKind::TyAlias(_, ref generics, ..) - | ItemKind::Trait(_, _, ref generics, ..) => { + | ItemKind::TyAlias(box TyAliasKind(_, ref generics, ..)) + | ItemKind::Trait(box TraitKind(_, _, ref generics, ..)) => { let def_id = self.lctx.resolver.local_def_id(item.id); let count = generics .params .iter() - .filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime { .. })) + .filter(|param| { + matches!(param.kind, ast::GenericParamKind::Lifetime { .. }) + }) .count(); self.lctx.type_def_lifetime_params.insert(def_id.to_def_id(), count); } @@ -701,10 +741,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: Span, allow_internal_unstable: Option>, ) -> Span { - span.fresh_expansion(ExpnData { - allow_internal_unstable, - ..ExpnData::default(ExpnKind::Desugaring(reason), span, self.sess.edition(), None) - }) + span.mark_with_reason(allow_internal_unstable, reason, self.sess.edition()) } fn with_anonymous_lifetime_mode( @@ -955,40 +992,75 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match *args { MacArgs::Empty => MacArgs::Empty, MacArgs::Delimited(dspan, delim, ref tokens) => { - MacArgs::Delimited(dspan, delim, self.lower_token_stream(tokens.clone())) - } - MacArgs::Eq(eq_span, ref tokens) => { - MacArgs::Eq(eq_span, self.lower_token_stream(tokens.clone())) + // This is either a non-key-value attribute, or a `macro_rules!` body. + // We either not have any nonterminals present (in the case of an attribute), + // or have tokens available for all nonterminals in the case of a nested + // `macro_rules`: e.g: + // + // ```rust + // macro_rules! outer { + // ($e:expr) => { + // macro_rules! inner { + // () => { $e } + // } + // } + // } + // ``` + // + // In both cases, we don't want to synthesize any tokens + MacArgs::Delimited( + dspan, + delim, + self.lower_token_stream(tokens.clone(), CanSynthesizeMissingTokens::No), + ) } - } - } - - fn lower_token_stream(&mut self, tokens: TokenStream) -> TokenStream { - tokens.into_trees().flat_map(|tree| self.lower_token_tree(tree).into_trees()).collect() - } + // This is an inert key-value attribute - it will never be visible to macros + // after it gets lowered to HIR. Therefore, we can synthesize tokens with fake + // spans to handle nonterminals in `#[doc]` (e.g. `#[doc = $e]`). + MacArgs::Eq(eq_span, ref token) => { + // In valid code the value is always representable as a single literal token. + fn unwrap_single_token(sess: &Session, tokens: TokenStream, span: Span) -> Token { + if tokens.len() != 1 { + sess.diagnostic() + .delay_span_bug(span, "multiple tokens in key-value attribute's value"); + } + match tokens.into_trees().next() { + Some(TokenTree::Token(token)) => token, + Some(TokenTree::Delimited(_, delim, tokens)) => { + if delim != token::NoDelim { + sess.diagnostic().delay_span_bug( + span, + "unexpected delimiter in key-value attribute's value", + ) + } + unwrap_single_token(sess, tokens, span) + } + None => Token::dummy(), + } + } - fn lower_token_tree(&mut self, tree: TokenTree) -> TokenStream { - match tree { - TokenTree::Token(token) => self.lower_token(token), - TokenTree::Delimited(span, delim, tts) => { - TokenTree::Delimited(span, delim, self.lower_token_stream(tts)).into() + let tokens = TokenStreamLowering { + parse_sess: &self.sess.parse_sess, + synthesize_tokens: CanSynthesizeMissingTokens::Yes, + nt_to_tokenstream: self.nt_to_tokenstream, + } + .lower_token(token.clone()); + MacArgs::Eq(eq_span, unwrap_single_token(self.sess, tokens, token.span)) } } } - fn lower_token(&mut self, token: Token) -> TokenStream { - match token.kind { - token::Interpolated(nt) => { - let tts = (self.nt_to_tokenstream)(&nt, &self.sess.parse_sess, token.span); - TokenTree::Delimited( - DelimSpan::from_single(token.span), - DelimToken::NoDelim, - self.lower_token_stream(tts), - ) - .into() - } - _ => TokenTree::Token(token).into(), + fn lower_token_stream( + &self, + tokens: TokenStream, + synthesize_tokens: CanSynthesizeMissingTokens, + ) -> TokenStream { + TokenStreamLowering { + parse_sess: &self.sess.parse_sess, + synthesize_tokens, + nt_to_tokenstream: self.nt_to_tokenstream, } + .lower_token_stream(tokens) } /// Given an associated type constraint like one of these: @@ -1004,16 +1076,40 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_assoc_ty_constraint( &mut self, constraint: &AssocTyConstraint, - itctx: ImplTraitContext<'_, 'hir>, + mut itctx: ImplTraitContext<'_, 'hir>, ) -> hir::TypeBinding<'hir> { debug!("lower_assoc_ty_constraint(constraint={:?}, itctx={:?})", constraint, itctx); - if let Some(ref gen_args) = constraint.gen_args { - self.sess.span_fatal( - gen_args.span(), - "generic associated types in trait paths are currently not implemented", - ); - } + // lower generic arguments of identifier in constraint + let gen_args = if let Some(ref gen_args) = constraint.gen_args { + let gen_args_ctor = match gen_args { + GenericArgs::AngleBracketed(ref data) => { + self.lower_angle_bracketed_parameter_data( + data, + ParamMode::Explicit, + itctx.reborrow(), + ) + .0 + } + GenericArgs::Parenthesized(ref data) => { + let mut err = self.sess.struct_span_err( + gen_args.span(), + "parenthesized generic arguments cannot be used in associated type constraints" + ); + // FIXME: try to write a suggestion here + err.emit(); + self.lower_angle_bracketed_parameter_data( + &data.as_angle_bracketed_args(), + ParamMode::Explicit, + itctx.reborrow(), + ) + .0 + } + }; + self.arena.alloc(gen_args_ctor.into_generic_args(&self.arena)) + } else { + self.arena.alloc(hir::GenericArgs::none()) + }; let kind = match constraint.kind { AssocTyConstraintKind::Equality { ref ty } => { @@ -1110,6 +1206,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TypeBinding { hir_id: self.lower_node_id(constraint.id), ident: constraint.ident, + gen_args, kind, span: constraint.span, } @@ -1220,6 +1317,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(&f.generic_params, |this| { this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| { + let span = this.sess.source_map().next_point(t.span.shrink_to_lo()); hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy { generic_params: this.lower_generic_params( &f.generic_params, @@ -1227,7 +1325,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::disallowed(), ), unsafety: this.lower_unsafety(f.unsafety), - abi: this.lower_extern(f.ext), + abi: this.lower_extern(f.ext, span, t.id), decl: this.lower_fn_decl(&f.decl, None, false, None), param_names: this.lower_fn_params_to_names(&f.decl), })) @@ -1716,7 +1814,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } self.arena.alloc_from_iter(inputs.iter().map(|param| match param.pat.kind { PatKind::Ident(_, ident, _) => ident, - _ => Ident::new(kw::Invalid, param.pat.span), + _ => Ident::new(kw::Empty, param.pat.span), })) } @@ -1806,12 +1904,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { output, c_variadic, implicit_self: decl.inputs.get(0).map_or(hir::ImplicitSelfKind::None, |arg| { - let is_mutable_pat = match arg.pat.kind { - PatKind::Ident(BindingMode::ByValue(mt) | BindingMode::ByRef(mt), _, _) => { - mt == Mutability::Mut - } - _ => false, - }; + use BindingMode::{ByRef, ByValue}; + let is_mutable_pat = matches!( + arg.pat.kind, + PatKind::Ident(ByValue(Mutability::Mut) | ByRef(Mutability::Mut), ..) + ); match arg.ty.kind { TyKind::ImplicitSelf if is_mutable_pat => hir::ImplicitSelfKind::Mut, @@ -2192,13 +2289,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (hir::ParamName::Plain(param.ident), kind) } - GenericParamKind::Const { ref ty, kw_span: _ } => { + GenericParamKind::Const { ref ty, kw_span: _, ref default } => { let ty = self .with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| { this.lower_ty(&ty, ImplTraitContext::disallowed()) }); + let default = default.as_ref().map(|def| self.lower_anon_const(def)); - (hir::ParamName::Plain(param.ident), hir::GenericParamKind::Const { ty }) + (hir::ParamName::Plain(param.ident), hir::GenericParamKind::Const { ty, default }) } }; @@ -2709,6 +2807,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } } + + fn maybe_lint_missing_abi(&mut self, span: Span, id: NodeId, default: Abi) { + // FIXME(davidtwco): This is a hack to detect macros which produce spans of the + // call site which do not have a macro backtrace. See #61963. + let is_macro_callsite = self + .sess + .source_map() + .span_to_snippet(span) + .map(|snippet| snippet.starts_with("#[")) + .unwrap_or(true); + if !is_macro_callsite { + self.resolver.lint_buffer().buffer_lint_with_diagnostic( + MISSING_ABI, + id, + span, + "extern declarations without an explicit ABI are deprecated", + BuiltinLintDiagnostics::MissingAbi(span, default), + ) + } + } } fn body_ids(bodies: &BTreeMap>) -> Vec { diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 6afed355dc..cb4d5ea6ee 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -273,7 +273,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { if !generic_args.parenthesized && !has_lifetimes { generic_args.args = self .elided_path_lifetimes( - first_generic_span.map(|s| s.shrink_to_lo()).unwrap_or(segment.ident.span), + first_generic_span.map_or(segment.ident.span, |s| s.shrink_to_lo()), expected_lifetimes, ) .map(GenericArg::Lifetime) @@ -362,7 +362,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_angle_bracketed_parameter_data( + pub(crate) fn lower_angle_bracketed_parameter_data( &mut self, data: &AngleBracketedArgs, param_mode: ParamMode, @@ -401,15 +401,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // compatibility, even in contexts like an impl header where // we generally don't permit such things (see #51008). self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| { - let &ParenthesizedArgs { ref inputs, ref output, span } = data; + let ParenthesizedArgs { span, inputs, inputs_span, output } = data; let inputs = this.arena.alloc_from_iter( inputs.iter().map(|ty| this.lower_ty_direct(ty, ImplTraitContext::disallowed())), ); let output_ty = match output { FnRetTy::Ty(ty) => this.lower_ty(&ty, ImplTraitContext::disallowed()), - FnRetTy::Default(_) => this.arena.alloc(this.ty_tup(span, &[])), + FnRetTy::Default(_) => this.arena.alloc(this.ty_tup(*span, &[])), }; - let args = smallvec![GenericArg::Type(this.ty_tup(span, inputs))]; + let args = smallvec![GenericArg::Type(this.ty_tup(*inputs_span, inputs))]; let binding = this.output_ty_binding(output_ty.span, output_ty); ( GenericArgsCtor { args, bindings: arena_vec![this; binding], parenthesized: true }, @@ -426,6 +426,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> hir::TypeBinding<'hir> { let ident = Ident::with_dummy_span(hir::FN_OUTPUT_NAME); let kind = hir::TypeBindingKind::Equality { ty }; - hir::TypeBinding { hir_id: self.next_id(), span, ident, kind } + let args = arena_vec![self;]; + let bindings = arena_vec![self;]; + let gen_args = self.arena.alloc(hir::GenericArgs { args, bindings, parenthesized: false }); + hir::TypeBinding { hir_id: self.next_id(), gen_args, span, ident, kind } } } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index bf6d332217..8defd91c68 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -16,7 +16,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{error_code, pluralize, struct_span_err, Applicability}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY; -use rustc_session::lint::LintBuffer; +use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; @@ -184,7 +184,7 @@ impl<'a> AstValidator<'a> { } fn check_lifetime(&self, ident: Ident) { - let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Invalid]; + let valid_names = [kw::UnderscoreLifetime, kw::StaticLifetime, kw::Empty]; if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() { self.err_handler().span_err(ident.span, "lifetimes cannot use keyword names"); } @@ -213,14 +213,14 @@ impl<'a> AstValidator<'a> { err.emit(); } - fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, bool)) { + fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option, bool)) { for Param { pat, .. } in &decl.inputs { match pat.kind { PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, None) | PatKind::Wild => {} - PatKind::Ident(BindingMode::ByValue(Mutability::Mut), _, None) => { - report_err(pat.span, true) + PatKind::Ident(BindingMode::ByValue(Mutability::Mut), ident, None) => { + report_err(pat.span, Some(ident), true) } - _ => report_err(pat.span, false), + _ => report_err(pat.span, None, false), } } } @@ -717,35 +717,46 @@ impl<'a> AstValidator<'a> { /// Checks that generic parameters are in the correct order, /// which is lifetimes, then types and then consts. (`<'a, T, const N: usize>`) -fn validate_generic_param_order<'a>( +fn validate_generic_param_order( sess: &Session, handler: &rustc_errors::Handler, - generics: impl Iterator, Span, Option)>, + generics: &[GenericParam], span: Span, ) { let mut max_param: Option = None; let mut out_of_order = FxHashMap::default(); let mut param_idents = vec![]; - for (kind, bounds, span, ident) in generics { + for param in generics { + let ident = Some(param.ident.to_string()); + let (kind, bounds, span) = (¶m.kind, Some(&*param.bounds), param.ident.span); + let (ord_kind, ident) = match ¶m.kind { + GenericParamKind::Lifetime => (ParamKindOrd::Lifetime, ident), + GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident), + GenericParamKind::Const { ref ty, kw_span: _, default: _ } => { + let ty = pprust::ty_to_string(ty); + let unordered = sess.features_untracked().const_generics; + (ParamKindOrd::Const { unordered }, Some(format!("const {}: {}", param.ident, ty))) + } + }; if let Some(ident) = ident { - param_idents.push((kind, bounds, param_idents.len(), ident)); + param_idents.push((kind, ord_kind, bounds, param_idents.len(), ident)); } let max_param = &mut max_param; match max_param { - Some(max_param) if *max_param > kind => { - let entry = out_of_order.entry(kind).or_insert((*max_param, vec![])); + Some(max_param) if *max_param > ord_kind => { + let entry = out_of_order.entry(ord_kind).or_insert((*max_param, vec![])); entry.1.push(span); } - Some(_) | None => *max_param = Some(kind), + Some(_) | None => *max_param = Some(ord_kind), }; } let mut ordered_params = "<".to_string(); if !out_of_order.is_empty() { - param_idents.sort_by_key(|&(po, _, i, _)| (po, i)); + param_idents.sort_by_key(|&(_, po, _, i, _)| (po, i)); let mut first = true; - for (_, bounds, _, ident) in param_idents { + for (kind, _, bounds, _, ident) in param_idents { if !first { ordered_params += ", "; } @@ -756,6 +767,16 @@ fn validate_generic_param_order<'a>( ordered_params += &pprust::bounds_to_string(&bounds); } } + match kind { + GenericParamKind::Type { default: Some(default) } => { + ordered_params += " = "; + ordered_params += &pprust::ty_to_string(default); + } + GenericParamKind::Type { default: None } => (), + GenericParamKind::Lifetime => (), + // FIXME(const_generics_defaults) + GenericParamKind::Const { ty: _, kw_span: _, default: _ } => (), + } first = false; } } @@ -773,14 +794,12 @@ fn validate_generic_param_order<'a>( err.span_suggestion( span, &format!( - "reorder the parameters: lifetimes{}", + "reorder the parameters: lifetimes, {}", if sess.features_untracked().const_generics { - ", then consts and types" - } else if sess.features_untracked().min_const_generics { - ", then types, then consts" + "then consts and types" } else { - ", then types" - }, + "then types, then consts" + } ), ordered_params.clone(), Applicability::MachineApplicable, @@ -815,7 +834,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { match ty.kind { TyKind::BareFn(ref bfty) => { self.check_fn_decl(&bfty.decl, SelfSemantic::No); - Self::check_decl_no_pat(&bfty.decl, |span, _| { + Self::check_decl_no_pat(&bfty.decl, |span, _, _| { struct_span_err!( self.session, span, @@ -901,7 +920,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } match item.kind { - ItemKind::Impl { + ItemKind::Impl(box ImplKind { unsafety, polarity, defaultness: _, @@ -910,7 +929,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { of_trait: Some(ref t), ref self_ty, items: _, - } => { + }) => { self.with_in_trait_impl(true, |this| { this.invalid_visibility(&item.vis, None); if let TyKind::Err = self_ty.kind { @@ -938,7 +957,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); return; // Avoid visiting again. } - ItemKind::Impl { + ItemKind::Impl(box ImplKind { unsafety, polarity, defaultness, @@ -947,7 +966,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { of_trait: None, ref self_ty, items: _, - } => { + }) => { let error = |annotation_span, annotation| { let mut err = self.err_handler().struct_span_err( self_ty.span, @@ -979,7 +998,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .emit(); } } - ItemKind::Fn(def, _, _, ref body) => { + ItemKind::Fn(box FnKind(def, _, _, ref body)) => { self.check_defaultness(item.span, def); if body.is_none() { @@ -1008,7 +1027,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } } - ItemKind::Trait(is_auto, _, ref generics, ref bounds, ref trait_items) => { + ItemKind::Trait(box TraitKind( + is_auto, + _, + ref generics, + ref bounds, + ref trait_items, + )) => { if is_auto == IsAuto::Yes { // Auto traits cannot have generics, super traits nor contain items. self.deny_generic_params(generics, item.ident.span); @@ -1056,7 +1081,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { let msg = "free static item without body"; self.error_item_without_body(item.span, "static", msg, " = ;"); } - ItemKind::TyAlias(def, _, ref bounds, ref body) => { + ItemKind::TyAlias(box TyAliasKind(def, _, ref bounds, ref body)) => { self.check_defaultness(item.span, def); if body.is_none() { let msg = "free type alias without body"; @@ -1072,12 +1097,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { match &fi.kind { - ForeignItemKind::Fn(def, sig, _, body) => { + ForeignItemKind::Fn(box FnKind(def, sig, _, body)) => { self.check_defaultness(fi.span, *def); self.check_foreign_fn_bodyless(fi.ident, body.as_deref()); self.check_foreign_fn_headerless(fi.ident, fi.span, sig.header); } - ForeignItemKind::TyAlias(def, generics, bounds, body) => { + ForeignItemKind::TyAlias(box TyAliasKind(def, generics, bounds, body)) => { self.check_defaultness(fi.span, *def); self.check_foreign_kind_bodyless(fi.ident, "type", body.as_ref().map(|b| b.span)); self.check_type_no_bounds(bounds, "`extern` blocks"); @@ -1152,22 +1177,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { validate_generic_param_order( self.session, self.err_handler(), - generics.params.iter().map(|param| { - let ident = Some(param.ident.to_string()); - let (kind, ident) = match ¶m.kind { - GenericParamKind::Lifetime => (ParamKindOrd::Lifetime, ident), - GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident), - GenericParamKind::Const { ref ty, kw_span: _ } => { - let ty = pprust::ty_to_string(ty); - let unordered = self.session.features_untracked().const_generics; - ( - ParamKindOrd::Const { unordered }, - Some(format!("const {}: {}", param.ident, ty)), - ) - } - }; - (kind, Some(&*param.bounds), param.ident.span, ident) - }), + &generics.params, generics.span, ); @@ -1208,11 +1218,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_pat(&mut self, pat: &'a Pat) { - match pat.kind { - PatKind::Lit(ref expr) => { + match &pat.kind { + PatKind::Lit(expr) => { self.check_expr_within_pat(expr, false); } - PatKind::Range(ref start, ref end, _) => { + PatKind::Range(start, end, _) => { if let Some(expr) = start { self.check_expr_within_pat(expr, true); } @@ -1285,7 +1295,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { // Functions without bodies cannot have patterns. if let FnKind::Fn(ctxt, _, sig, _, None) = fk { - Self::check_decl_no_pat(&sig.decl, |span, mut_ident| { + Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| { let (code, msg, label) = match ctxt { FnCtxt::Foreign => ( error_code!(E0130), @@ -1299,7 +1309,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ), }; if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) { - self.lint_buffer.buffer_lint(PATTERNS_IN_FNS_WITHOUT_BODY, id, span, msg); + if let Some(ident) = ident { + let diag = BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident); + self.lint_buffer.buffer_lint_with_diagnostic( + PATTERNS_IN_FNS_WITHOUT_BODY, + id, + span, + msg, + diag, + ) + } } else { self.err_handler() .struct_span_err(span, msg) @@ -1323,10 +1342,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { AssocItemKind::Const(_, _, body) => { self.check_impl_item_provided(item.span, body, "constant", " = ;"); } - AssocItemKind::Fn(_, _, _, body) => { + AssocItemKind::Fn(box FnKind(_, _, _, body)) => { self.check_impl_item_provided(item.span, body, "function", " { }"); } - AssocItemKind::TyAlias(_, _, bounds, body) => { + AssocItemKind::TyAlias(box TyAliasKind(_, _, bounds, body)) => { self.check_impl_item_provided(item.span, body, "type", " = ;"); self.check_type_no_bounds(bounds, "`impl`s"); } @@ -1336,7 +1355,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if ctxt == AssocCtxt::Trait || self.in_trait_impl { self.invalid_visibility(&item.vis, None); - if let AssocItemKind::Fn(_, sig, _, _) = &item.kind { + if let AssocItemKind::Fn(box FnKind(_, sig, _, _)) = &item.kind { self.check_trait_fn_not_const(sig.header.constness); self.check_trait_fn_not_async(item.span, sig.header.asyncness); } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index bb22267523..6514de2b81 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,7 +1,7 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{AssocTyConstraint, AssocTyConstraintKind, NodeId}; -use rustc_ast::{GenericParam, GenericParamKind, PatKind, RangeEnd, VariantData}; +use rustc_ast::{PatKind, RangeEnd, VariantData}; use rustc_errors::struct_span_err; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP}; use rustc_feature::{Features, GateIssue}; @@ -14,6 +14,17 @@ use rustc_span::Span; use tracing::debug; macro_rules! gate_feature_fn { + ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{ + let (visitor, has_feature, span, name, explain, help) = + (&*$visitor, $has_feature, $span, $name, $explain, $help); + let has_feature: bool = has_feature(visitor.features); + debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature); + if !has_feature && !span.allows_unstable($name) { + feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain) + .help(help) + .emit(); + } + }}; ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{ let (visitor, has_feature, span, name, explain) = (&*$visitor, $has_feature, $span, $name, $explain); @@ -27,6 +38,9 @@ macro_rules! gate_feature_fn { } macro_rules! gate_feature_post { + ($visitor: expr, $feature: ident, $span: expr, $explain: expr, $help: expr) => { + gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain, $help) + }; ($visitor: expr, $feature: ident, $span: expr, $explain: expr) => { gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain) }; @@ -142,6 +156,14 @@ impl<'a> PostExpansionVisitor<'a> { "efiapi ABI is experimental and subject to change" ); } + "C-cmse-nonsecure-call" => { + gate_feature_post!( + &self, + abi_c_cmse_nonsecure_call, + span, + "C-cmse-nonsecure-call ABI is experimental and subject to change" + ); + } abi => self .sess .parse_sess @@ -351,12 +373,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - ast::ItemKind::Impl { polarity, defaultness, ref of_trait, .. } => { + ast::ItemKind::Impl(box ast::ImplKind { + polarity, defaultness, ref of_trait, .. + }) => { if let ast::ImplPolarity::Negative(span) = polarity { gate_feature_post!( &self, negative_impls, - span.to(of_trait.as_ref().map(|t| t.path.span).unwrap_or(span)), + span.to(of_trait.as_ref().map_or(span, |t| t.path.span)), "negative trait bounds are not yet fully implemented; \ use marker types for now" ); @@ -367,7 +391,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => { + ast::ItemKind::Trait(box ast::TraitKind(ast::IsAuto::Yes, ..)) => { gate_feature_post!( &self, auto_traits, @@ -385,7 +409,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, decl_macro, i.span, msg); } - ast::ItemKind::TyAlias(_, _, _, Some(ref ty)) => self.check_impl_trait(&ty), + ast::ItemKind::TyAlias(box ast::TyAliasKind(_, _, _, Some(ref ty))) => { + self.check_impl_trait(&ty) + } _ => {} } @@ -397,10 +423,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { match i.kind { ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => { let link_name = self.sess.first_attr_value_str_by_name(&i.attrs, sym::link_name); - let links_to_llvm = match link_name { - Some(val) => val.as_str().starts_with("llvm."), - _ => false, - }; + let links_to_llvm = + link_name.map_or(false, |val| val.as_str().starts_with("llvm.")); if links_to_llvm { gate_feature_post!( &self, @@ -529,19 +553,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_fn(self, fn_kind, span) } - fn visit_generic_param(&mut self, param: &'a GenericParam) { - if let GenericParamKind::Const { .. } = param.kind { - gate_feature_fn!( - &self, - |x: &Features| x.const_generics || x.min_const_generics, - param.ident.span, - sym::min_const_generics, - "const generics are unstable" - ); - } - visit::walk_generic_param(self, param) - } - fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) { if let AssocTyConstraintKind::Bound { .. } = constraint.kind { gate_feature_post!( @@ -556,13 +567,13 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) { let is_fn = match i.kind { - ast::AssocItemKind::Fn(_, ref sig, _, _) => { + ast::AssocItemKind::Fn(box ast::FnKind(_, ref sig, _, _)) => { if let (ast::Const::Yes(_), AssocCtxt::Trait) = (sig.header.constness, ctxt) { gate_feature_post!(&self, const_fn, i.span, "const fn is unstable"); } true } - ast::AssocItemKind::TyAlias(_, ref generics, _, ref ty) => { + ast::AssocItemKind::TyAlias(box ast::TyAliasKind(_, ref generics, _, ref ty)) => { if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) { gate_feature_post!( &self, @@ -612,6 +623,13 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { let spans = sess.parse_sess.gated_spans.spans.borrow(); macro_rules! gate_all { + ($gate:ident, $msg:literal, $help:literal) => { + if let Some(spans) = spans.get(&sym::$gate) { + for span in spans { + gate_feature_post!(&visitor, $gate, *span, $msg, $help); + } + } + }; ($gate:ident, $msg:literal) => { if let Some(spans) = spans.get(&sym::$gate) { for span in spans { @@ -622,7 +640,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { } gate_all!(if_let_guard, "`if let` guards are experimental"); gate_all!(let_chains, "`let` expressions in this position are experimental"); - gate_all!(async_closure, "async closures are unstable"); + gate_all!( + async_closure, + "async closures are unstable", + "to use an async block, remove the `||`: `async {`" + ); gate_all!(generators, "yield syntax is experimental"); gate_all!(or_patterns, "or-patterns syntax is experimental"); gate_all!(raw_ref_op, "raw address of syntax is experimental"); @@ -634,6 +656,10 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { extended_key_value_attributes, "arbitrary expressions in key-value attributes are unstable" ); + gate_all!( + const_generics_defaults, + "default values for const generic parameters are experimental" + ); if sess.parse_sess.span_diagnostic.err_count() == 0 { // Errors for `destructuring_assignment` can get quite noisy, especially where `_` is // involved, so we only emit errors where there are no other parsing errors. diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 7487421e70..c9e2d202da 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -6,6 +6,8 @@ #![feature(bindings_after_at)] #![feature(iter_is_partitioned)] +#![feature(box_syntax)] +#![feature(box_patterns)] #![recursion_limit = "256"] pub mod ast_validation; diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs index 6efc78c884..2971fa435c 100644 --- a/compiler/rustc_ast_passes/src/node_count.rs +++ b/compiler/rustc_ast_passes/src/node_count.rs @@ -68,7 +68,7 @@ impl<'ast> Visitor<'ast> for NodeCounter { self.count += 1; walk_generics(self, g) } - fn visit_fn(&mut self, fk: FnKind<'_>, s: Span, _: NodeId) { + fn visit_fn(&mut self, fk: visit::FnKind<'_>, s: Span, _: NodeId) { self.count += 1; walk_fn(self, fk, s) } diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml index f447bc7f4e..6ea942a2f3 100644 --- a/compiler/rustc_ast_pretty/Cargo.toml +++ b/compiler/rustc_ast_pretty/Cargo.toml @@ -11,4 +11,3 @@ doctest = false tracing = "0.1" rustc_span = { path = "../rustc_span" } rustc_ast = { path = "../rustc_ast" } -rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs index 9adc6c604e..d869baad01 100644 --- a/compiler/rustc_ast_pretty/src/lib.rs +++ b/compiler/rustc_ast_pretty/src/lib.rs @@ -1,6 +1,7 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(or_patterns)] +#![feature(box_patterns)] #![recursion_limit = "256"] mod helpers; diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index 56e769ba6b..ea298d28e7 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -75,7 +75,7 @@ //! breaking inconsistently to become //! //! ``` -//! foo(hello, there +//! foo(hello, there, //! good, friends); //! ``` //! @@ -83,7 +83,7 @@ //! //! ``` //! foo(hello, -//! there +//! there, //! good, //! friends); //! ``` diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index b34ea41ab5..b88699f6ee 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -8,11 +8,6 @@ use rustc_ast as ast; use rustc_ast::token::{Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; -pub fn nonterminal_to_string_no_extra_parens(nt: &Nonterminal) -> String { - let state = State::without_insert_extra_parens(); - state.nonterminal_to_string(nt) -} - pub fn nonterminal_to_string(nt: &Nonterminal) -> String { State::new().nonterminal_to_string(nt) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index dcb6e115ed..7f4775bf41 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -88,13 +88,6 @@ pub struct State<'a> { comments: Option>, ann: &'a (dyn PpAnn + 'a), is_expanded: bool, - // If `true`, additional parenthesis (separate from `ExprKind::Paren`) - // are inserted to ensure that proper precedence is preserved - // in the pretty-printed output. - // - // This is usually `true`, except when performing the pretty-print/reparse - // check in `nt_to_tokenstream` - insert_extra_parens: bool, } crate const INDENT_UNIT: usize = 4; @@ -115,7 +108,6 @@ pub fn print_crate<'a>( comments: Some(Comments::new(sm, filename, input)), ann, is_expanded, - insert_extra_parens: true, }; if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) { @@ -235,7 +227,6 @@ impl std::ops::DerefMut for State<'_> { } pub trait PrintState<'a>: std::ops::Deref + std::ops::DerefMut { - fn insert_extra_parens(&self) -> bool; fn comments(&mut self) -> &mut Option>; fn print_ident(&mut self, ident: Ident); fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool); @@ -454,10 +445,11 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere ), MacArgs::Empty | MacArgs::Eq(..) => { self.print_path(&item.path, false, 0); - if let MacArgs::Eq(_, tokens) = &item.args { + if let MacArgs::Eq(_, token) = &item.args { self.space(); self.word_space("="); - self.print_tts(tokens, true); + let token_str = self.token_to_string_ext(token, true); + self.word(token_str); } } } @@ -819,16 +811,12 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere fn to_string(&self, f: impl FnOnce(&mut State<'_>)) -> String { let mut printer = State::new(); - printer.insert_extra_parens = self.insert_extra_parens(); f(&mut printer); printer.s.eof() } } impl<'a> PrintState<'a> for State<'a> { - fn insert_extra_parens(&self) -> bool { - self.insert_extra_parens - } fn comments(&mut self) -> &mut Option> { &mut self.comments } @@ -865,17 +853,7 @@ impl<'a> PrintState<'a> for State<'a> { impl<'a> State<'a> { pub fn new() -> State<'a> { - State { - s: pp::mk_printer(), - comments: None, - ann: &NoAnn, - is_expanded: false, - insert_extra_parens: true, - } - } - - pub(super) fn without_insert_extra_parens() -> State<'a> { - State { insert_extra_parens: false, ..State::new() } + State { s: pp::mk_printer(), comments: None, ann: &NoAnn, is_expanded: false } } // Synthesizes a comment that was not textually present in the original source @@ -1044,14 +1022,14 @@ impl<'a> State<'a> { self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::ForeignItemKind::Fn(def, sig, gen, body) => { + ast::ForeignItemKind::Fn(box ast::FnKind(def, sig, gen, body)) => { self.print_fn_full(sig, ident, gen, vis, *def, body.as_deref(), attrs); } ast::ForeignItemKind::Static(ty, mutbl, body) => { let def = ast::Defaultness::Final; self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def); } - ast::ForeignItemKind::TyAlias(def, generics, bounds, ty) => { + ast::ForeignItemKind::TyAlias(box ast::TyAliasKind(def, generics, bounds, ty)) => { self.print_associated_type(ident, generics, bounds, ty.as_deref(), vis, *def); } ast::ForeignItemKind::MacCall(m) => { @@ -1156,7 +1134,7 @@ impl<'a> State<'a> { ast::ItemKind::Const(def, ref ty, ref body) => { self.print_item_const(item.ident, None, ty, body.as_deref(), &item.vis, def); } - ast::ItemKind::Fn(def, ref sig, ref gen, ref body) => { + ast::ItemKind::Fn(box ast::FnKind(def, ref sig, ref gen, ref body)) => { let body = body.as_deref(); self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs); } @@ -1197,7 +1175,7 @@ impl<'a> State<'a> { self.s.word(ga.asm.to_string()); self.end(); } - ast::ItemKind::TyAlias(def, ref generics, ref bounds, ref ty) => { + ast::ItemKind::TyAlias(box ast::TyAliasKind(def, ref generics, ref bounds, ref ty)) => { let ty = ty.as_deref(); self.print_associated_type(item.ident, generics, bounds, ty, &item.vis, def); } @@ -1212,7 +1190,7 @@ impl<'a> State<'a> { self.head(visibility_qualified(&item.vis, "union")); self.print_struct(struct_def, generics, item.ident, item.span, true); } - ast::ItemKind::Impl { + ast::ItemKind::Impl(box ast::ImplKind { unsafety, polarity, defaultness, @@ -1221,7 +1199,7 @@ impl<'a> State<'a> { ref of_trait, ref self_ty, ref items, - } => { + }) => { self.head(""); self.print_visibility(&item.vis); self.print_defaultness(defaultness); @@ -1255,7 +1233,13 @@ impl<'a> State<'a> { } self.bclose(item.span); } - ast::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref trait_items) => { + ast::ItemKind::Trait(box ast::TraitKind( + is_auto, + unsafety, + ref generics, + ref bounds, + ref trait_items, + )) => { self.head(""); self.print_visibility(&item.vis); self.print_unsafety(unsafety); @@ -1475,13 +1459,13 @@ impl<'a> State<'a> { self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::AssocItemKind::Fn(def, sig, gen, body) => { + ast::AssocItemKind::Fn(box ast::FnKind(def, sig, gen, body)) => { self.print_fn_full(sig, ident, gen, vis, *def, body.as_deref(), attrs); } ast::AssocItemKind::Const(def, ty, body) => { self.print_item_const(ident, None, ty, body.as_deref(), vis, *def); } - ast::AssocItemKind::TyAlias(def, generics, bounds, ty) => { + ast::AssocItemKind::TyAlias(box ast::TyAliasKind(def, generics, bounds, ty)) => { self.print_associated_type(ident, generics, bounds, ty.as_deref(), vis, *def); } ast::AssocItemKind::MacCall(m) => { @@ -1680,8 +1664,7 @@ impl<'a> State<'a> { } /// Prints `expr` or `(expr)` when `needs_par` holds. - fn print_expr_cond_paren(&mut self, expr: &ast::Expr, mut needs_par: bool) { - needs_par &= self.insert_extra_parens; + fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) { if needs_par { self.popen(); } @@ -2668,13 +2651,16 @@ impl<'a> State<'a> { s.print_type(default) } } - ast::GenericParamKind::Const { ref ty, kw_span: _ } => { + ast::GenericParamKind::Const { ref ty, kw_span: _, ref default } => { s.word_space("const"); s.print_ident(param.ident); s.s.space(); s.word_space(":"); s.print_type(ty); - s.print_type_bounds(":", ¶m.bounds) + s.print_type_bounds(":", ¶m.bounds); + if let Some(ref _default) = default { + // FIXME(const_generics_defaults): print the `default` value here + } } } }); @@ -2787,7 +2773,7 @@ impl<'a> State<'a> { self.print_explicit_self(&eself); } else { let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind { - ident.name == kw::Invalid + ident.name == kw::Empty } else { false }; diff --git a/compiler/rustc_attr/Cargo.toml b/compiler/rustc_attr/Cargo.toml index 5f941a0a65..dc0711a5b0 100644 --- a/compiler/rustc_attr/Cargo.toml +++ b/compiler/rustc_attr/Cargo.toml @@ -18,4 +18,3 @@ rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_session = { path = "../rustc_session" } rustc_ast = { path = "../rustc_ast" } -version_check = "0.9" diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index ead90f23ce..aca3fbbca1 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -10,7 +10,6 @@ use rustc_session::Session; use rustc_span::hygiene::Transparency; use rustc_span::{symbol::sym, symbol::Symbol, Span}; use std::num::NonZeroU32; -use version_check::Version; pub fn is_builtin_attr(attr: &Attribute) -> bool { attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some() @@ -67,7 +66,7 @@ fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) { } } -#[derive(Copy, Clone, PartialEq, Encodable, Decodable)] +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug)] pub enum InlineAttr { None, Hint, @@ -75,13 +74,13 @@ pub enum InlineAttr { Never, } -#[derive(Clone, Encodable, Decodable)] +#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)] pub enum InstructionSetAttr { ArmA32, ArmT32, } -#[derive(Clone, Encodable, Decodable)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum OptimizeAttr { None, Speed, @@ -526,6 +525,26 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F } } +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +struct Version { + major: u16, + minor: u16, + patch: u16, +} + +fn parse_version(s: &str, allow_appendix: bool) -> Option { + let mut components = s.split('-'); + let d = components.next()?; + if !allow_appendix && components.next().is_some() { + return None; + } + let mut digits = d.splitn(3, '.'); + let major = digits.next()?.parse().ok()?; + let minor = digits.next()?.parse().ok()?; + let patch = digits.next().unwrap_or("0").parse().ok()?; + Some(Version { major, minor, patch }) +} + /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to /// evaluate individual items. pub fn eval_condition( @@ -555,19 +574,26 @@ pub fn eval_condition( return false; } }; - let min_version = match Version::parse(&min_version.as_str()) { + let min_version = match parse_version(&min_version.as_str(), false) { Some(ver) => ver, None => { - sess.span_diagnostic.struct_span_err(*span, "invalid version literal").emit(); + sess.span_diagnostic + .struct_span_warn( + *span, + "unknown version literal format, assuming it refers to a future version", + ) + .emit(); return false; } }; - let channel = env!("CFG_RELEASE_CHANNEL"); - let nightly = channel == "nightly" || channel == "dev"; - let rustc_version = Version::parse(env!("CFG_RELEASE")).unwrap(); + let rustc_version = parse_version(env!("CFG_RELEASE"), true).unwrap(); - // See https://github.com/rust-lang/rust/issues/64796#issuecomment-625474439 for details - if nightly { rustc_version > min_version } else { rustc_version >= min_version } + // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details + if sess.assume_incomplete_release { + rustc_version > min_version + } else { + rustc_version >= min_version + } } ast::MetaItemKind::List(ref mis) => { for mi in mis.iter() { diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index bb6d3f6a00..93ba54da34 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -12,27 +12,43 @@ use rustc_span::{Span, DUMMY_SP}; pub fn expand_assert<'cx>( cx: &'cx mut ExtCtxt<'_>, - sp: Span, + span: Span, tts: TokenStream, ) -> Box { - let Assert { cond_expr, custom_message } = match parse_assert(cx, sp, tts) { + let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) { Ok(assert) => assert, Err(mut err) => { err.emit(); - return DummyResult::any(sp); + return DummyResult::any(span); } }; // `core::panic` and `std::panic` are different macros, so we use call-site // context to pick up whichever is currently in scope. - let sp = cx.with_call_site_ctxt(sp); + let sp = cx.with_call_site_ctxt(span); let panic_call = if let Some(tokens) = custom_message { + let path = if span.rust_2021() { + // On edition 2021, we always call `$crate::panic::panic_2021!()`. + Path { + span: sp, + segments: cx + .std_path(&[sym::panic, sym::panic_2021]) + .into_iter() + .map(|ident| PathSegment::from_ident(ident)) + .collect(), + tokens: None, + } + } else { + // Before edition 2021, we call `panic!()` unqualified, + // such that it calls either `std::panic!()` or `core::panic!()`. + Path::from_ident(Ident::new(sym::panic, sp)) + }; // Pass the custom message to panic!(). cx.expr( sp, ExprKind::MacCall(MacCall { - path: Path::from_ident(Ident::new(sym::panic, sp)), + path, args: P(MacArgs::Delimited( DelimSpan::from_single(sp), MacDelimiter::Parenthesis, diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 957c803539..ca1226b445 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -38,10 +38,9 @@ pub fn expand_deriving_clone( | ItemKind::Enum(_, Generics { ref params, .. }) => { let container_id = cx.current_expansion.id.expn_data().parent; if cx.resolver.has_derive_copy(container_id) - && !params.iter().any(|param| match param.kind { - ast::GenericParamKind::Type { .. } => true, - _ => false, - }) + && !params + .iter() + .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) { bounds = vec![]; is_shallow = true; diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 5c21329069..ba43be6ae9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -8,6 +8,10 @@ use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; +fn make_mut_borrow(cx: &mut ExtCtxt<'_>, sp: Span, expr: P) -> P { + cx.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Mut, expr)) +} + pub fn expand_deriving_debug( cx: &mut ExtCtxt<'_>, span: Span, @@ -67,11 +71,12 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> let fmt = substr.nonself_args[0].clone(); let mut stmts = Vec::with_capacity(fields.len() + 2); + let fn_path_finish; match vdata { ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => { // tuple struct/"normal" variant - let expr = - cx.expr_method_call(span, fmt, Ident::new(sym::debug_tuple, span), vec![name]); + let fn_path_debug_tuple = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_tuple]); + let expr = cx.expr_call_global(span, fn_path_debug_tuple, vec![fmt, name]); stmts.push(cx.stmt_let(span, true, builder, expr)); for field in fields { @@ -79,22 +84,21 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> let field = cx.expr_addr_of(field.span, field.self_.clone()); let field = cx.expr_addr_of(field.span, field); - let expr = cx.expr_method_call( - span, - builder_expr.clone(), - Ident::new(sym::field, span), - vec![field], - ); + let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::field]); + let builder_recv = make_mut_borrow(cx, span, builder_expr.clone()); + let expr = cx.expr_call_global(span, fn_path_field, vec![builder_recv, field]); // Use `let _ = expr;` to avoid triggering the // unused_results lint. stmts.push(stmt_let_underscore(cx, span, expr)); } + + fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::finish]); } ast::VariantData::Struct(..) => { // normal struct/struct variant - let expr = - cx.expr_method_call(span, fmt, Ident::new(sym::debug_struct, span), vec![name]); + let fn_path_debug_struct = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_struct]); + let expr = cx.expr_call_global(span, fn_path_debug_struct, vec![fmt, name]); stmts.push(cx.stmt_let(DUMMY_SP, true, builder, expr)); for field in fields { @@ -104,20 +108,20 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> ); // Use double indirection to make sure this works for unsized types + let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::field]); let field = cx.expr_addr_of(field.span, field.self_.clone()); let field = cx.expr_addr_of(field.span, field); - let expr = cx.expr_method_call( - span, - builder_expr.clone(), - Ident::new(sym::field, span), - vec![name, field], - ); + let builder_recv = make_mut_borrow(cx, span, builder_expr.clone()); + let expr = + cx.expr_call_global(span, fn_path_field, vec![builder_recv, name, field]); stmts.push(stmt_let_underscore(cx, span, expr)); } + fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::finish]); } } - let expr = cx.expr_method_call(span, builder_expr, Ident::new(sym::finish, span), vec![]); + let builder_recv = make_mut_borrow(cx, span, builder_expr); + let expr = cx.expr_call_global(span, fn_path_finish, vec![builder_recv]); stmts.push(cx.stmt_expr(expr)); let block = cx.block(span, stmts); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 6531e68be9..d498c8e172 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -404,12 +404,10 @@ impl<'a> TraitDef<'a> { let has_no_type_params = match item.kind { ast::ItemKind::Struct(_, ref generics) | ast::ItemKind::Enum(_, ref generics) - | ast::ItemKind::Union(_, ref generics) => { - !generics.params.iter().any(|param| match param.kind { - ast::GenericParamKind::Type { .. } => true, - _ => false, - }) - } + | ast::ItemKind::Union(_, ref generics) => !generics + .params + .iter() + .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })), _ => unreachable!(), }; let container_id = cx.current_expansion.id.expn_data().parent; @@ -529,12 +527,12 @@ impl<'a> TraitDef<'a> { tokens: None, }, attrs: Vec::new(), - kind: ast::AssocItemKind::TyAlias( + kind: ast::AssocItemKind::TyAlias(box ast::TyAliasKind( ast::Defaultness::Final, Generics::default(), Vec::new(), Some(type_def.to_ty(cx, self.span, type_ident, generics)), - ), + )), tokens: None, }) }); @@ -600,7 +598,7 @@ impl<'a> TraitDef<'a> { let mut ty_params = params .iter() - .filter(|param| matches!(param.kind, ast::GenericParamKind::Type{..})) + .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) .peekable(); if ty_params.peek().is_some() { @@ -689,7 +687,7 @@ impl<'a> TraitDef<'a> { self.span, Ident::invalid(), a, - ast::ItemKind::Impl { + ast::ItemKind::Impl(box ast::ImplKind { unsafety, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, @@ -698,7 +696,7 @@ impl<'a> TraitDef<'a> { of_trait: opt_trait_ref, self_ty: self_type, items: methods.into_iter().chain(associated_types).collect(), - }, + }), ) } @@ -868,7 +866,7 @@ impl<'a> MethodDef<'a> { Self_ if nonstatic => { self_args.push(arg_expr); } - Ptr(ref ty, _) if (if let Self_ = **ty { true } else { false }) && nonstatic => { + Ptr(ref ty, _) if matches!(**ty, Self_) && nonstatic => { self_args.push(cx.expr_deref(trait_.span, arg_expr)) } _ => { @@ -931,7 +929,7 @@ impl<'a> MethodDef<'a> { tokens: None, }, ident: method_ident, - kind: ast::AssocItemKind::Fn(def, sig, fn_generics, Some(body_block)), + kind: ast::AssocItemKind::Fn(box ast::FnKind(def, sig, fn_generics, Some(body_block))), tokens: None, }) } diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index 868f863b99..7114b98768 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -48,8 +48,8 @@ pub fn expand_deriving_hash( } fn hash_substructure(cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>) -> P { - let state_expr = match &substr.nonself_args { - &[o_f] => o_f, + let state_expr = match substr.nonself_args { + [o_f] => o_f, _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`"), }; let call_hash = |span, thing_expr| { @@ -64,9 +64,9 @@ fn hash_substructure(cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructu }; let mut stmts = Vec::new(); - let fields = match *substr.fields { - Struct(_, ref fs) | EnumMatching(_, 1, .., ref fs) => fs, - EnumMatching(.., ref fs) => { + let fields = match substr.fields { + Struct(_, fs) | EnumMatching(_, 1, .., fs) => fs, + EnumMatching(.., fs) => { let variant_value = deriving::call_intrinsic( cx, trait_span, diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 1651180817..7dea6099f8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -2,7 +2,7 @@ use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::{ItemKind, MetaItem}; +use rustc_ast::{ImplKind, ItemKind, MetaItem}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; @@ -145,7 +145,8 @@ fn inject_impl_of_structural_trait( *default = None; ast::GenericArg::Type(cx.ty_ident(span, param.ident)) } - ast::GenericParamKind::Const { ty: _, kw_span: _ } => { + ast::GenericParamKind::Const { ty: _, kw_span: _, default } => { + *default = None; ast::GenericArg::Const(cx.const_ident(span, param.ident)) } }) @@ -178,7 +179,7 @@ fn inject_impl_of_structural_trait( span, Ident::invalid(), attrs, - ItemKind::Impl { + ItemKind::Impl(box ImplKind { unsafety: ast::Unsafe::No, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, @@ -187,7 +188,7 @@ fn inject_impl_of_structural_trait( of_trait: Some(trait_ref), self_ty: self_type, items: Vec::new(), - }, + }), ); push(Annotatable::Item(newitem)); diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 550524e652..85ca1da6f1 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1044,10 +1044,7 @@ pub fn expand_preparsed_format_args( let numbered_position_args = pieces.iter().any(|arg: &parse::Piece<'_>| match *arg { parse::String(_) => false, - parse::NextArgument(arg) => match arg.position { - parse::Position::ArgumentIs(_) => true, - _ => false, - }, + parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(_)), }); cx.build_index_map(); diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs index f00dfd1241..0496c72cb0 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign.rs @@ -580,10 +580,7 @@ pub mod printf { } fn is_flag(c: &char) -> bool { - match c { - '0' | '-' | '+' | ' ' | '#' | '\'' => true, - _ => false, - } + matches!(c, '0' | '-' | '+' | ' ' | '#' | '\'') } #[cfg(test)] diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index e976805d9d..9b43c11f0f 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -5,7 +5,7 @@ use rustc_ast::expand::allocator::{ }; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Attribute, Expr, FnHeader, FnSig, Generics, Param, StmtKind}; -use rustc_ast::{ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe}; +use rustc_ast::{FnKind, ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -85,7 +85,8 @@ impl AllocFnFactory<'_, '_> { let header = FnHeader { unsafety: Unsafe::Yes(self.span), ..FnHeader::default() }; let sig = FnSig { decl, header, span: self.span }; let block = Some(self.cx.block_expr(output_expr)); - let kind = ItemKind::Fn(ast::Defaultness::Final, sig, Generics::default(), block); + let kind = + ItemKind::Fn(box FnKind(ast::Defaultness::Final, sig, Generics::default(), block)); let item = self.cx.item( self.span, Ident::from_str_and_span(&self.kind.fn_name(method.name), self.span), diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 97cadb913c..59844b6c69 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -2,6 +2,8 @@ //! injecting code into the crate before it is lowered to HIR. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(box_patterns)] +#![feature(box_syntax)] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] @@ -14,10 +16,9 @@ extern crate proc_macro; use crate::deriving::*; -use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtension, SyntaxExtensionKind}; +use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind}; use rustc_expand::proc_macro::BangProcMacro; -use rustc_span::edition::Edition; -use rustc_span::symbol::{sym, Ident}; +use rustc_span::symbol::sym; mod asm; mod assert; @@ -34,6 +35,7 @@ mod global_allocator; mod global_asm; mod llvm_asm; mod log_syntax; +mod panic; mod source_util; mod test; mod trace_macros; @@ -44,13 +46,8 @@ pub mod proc_macro_harness; pub mod standard_library_imports; pub mod test_harness; -pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand, edition: Edition) { - let mut register = |name, kind| { - resolver.register_builtin_macro( - Ident::with_dummy_span(name), - SyntaxExtension { is_builtin: true, ..SyntaxExtension::default(kind, edition) }, - ) - }; +pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { + let mut register = |name, kind| resolver.register_builtin_macro(name, kind); macro register_bang($($name:ident: $f:expr,)*) { $(register(sym::$name, SyntaxExtensionKind::LegacyBang(Box::new($f as MacroExpanderFn)));)* } @@ -82,6 +79,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand, edition: Editi log_syntax: log_syntax::expand_log_syntax, module_path: source_util::expand_mod, option_env: env::expand_option_env, + core_panic: panic::expand_panic, + std_panic: panic::expand_panic, stringify: source_util::expand_stringify, trace_macros: trace_macros::expand_trace_macros, } diff --git a/compiler/rustc_builtin_macros/src/llvm_asm.rs b/compiler/rustc_builtin_macros/src/llvm_asm.rs index db73fdbe24..d72bfa660e 100644 --- a/compiler/rustc_builtin_macros/src/llvm_asm.rs +++ b/compiler/rustc_builtin_macros/src/llvm_asm.rs @@ -87,13 +87,15 @@ fn parse_inline_asm<'a>( // parsed as `llvm_asm!(z)` with `z = "x": y` which is type ascription. let first_colon = tts .trees() - .position(|tt| match tt { - tokenstream::TokenTree::Token(Token { kind: token::Colon | token::ModSep, .. }) => true, - _ => false, + .position(|tt| { + matches!( + tt, + tokenstream::TokenTree::Token(Token { kind: token::Colon | token::ModSep, .. }) + ) }) .unwrap_or(tts.len()); let mut p = cx.new_parser_from_tts(tts.trees().skip(first_colon).collect()); - let mut asm = kw::Invalid; + let mut asm = kw::Empty; let mut asm_str_style = None; let mut outputs = Vec::new(); let mut inputs = Vec::new(); diff --git a/compiler/rustc_builtin_macros/src/panic.rs b/compiler/rustc_builtin_macros/src/panic.rs new file mode 100644 index 0000000000..6f5962d435 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/panic.rs @@ -0,0 +1,48 @@ +use rustc_ast::ptr::P; +use rustc_ast::tokenstream::{DelimSpan, TokenStream}; +use rustc_ast::*; +use rustc_expand::base::*; +use rustc_span::symbol::sym; +use rustc_span::Span; + +// This expands to either +// - `$crate::panic::panic_2015!(...)` or +// - `$crate::panic::panic_2021!(...)` +// depending on the edition. +// +// This is used for both std::panic!() and core::panic!(). +// +// `$crate` will refer to either the `std` or `core` crate depending on which +// one we're expanding from. +pub fn expand_panic<'cx>( + cx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> Box { + let panic = if sp.rust_2021() { sym::panic_2021 } else { sym::panic_2015 }; + + let sp = cx.with_call_site_ctxt(sp); + + MacEager::expr( + cx.expr( + sp, + ExprKind::MacCall(MacCall { + path: Path { + span: sp, + segments: cx + .std_path(&[sym::panic, panic]) + .into_iter() + .map(|ident| PathSegment::from_ident(ident)) + .collect(), + tokens: None, + }, + args: P(MacArgs::Delimited( + DelimSpan::from_single(sp), + MacDelimiter::Parenthesis, + tts, + )), + prior_type_ascription: None, + }), + ), + ) +} diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 4e91436199..7582d98053 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -256,10 +256,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { // we're just not interested in this item. // // If we find one, try to locate a `#[proc_macro_derive]` attribute on it. - let is_fn = match item.kind { - ast::ItemKind::Fn(..) => true, - _ => false, - }; + let is_fn = matches!(item.kind, ast::ItemKind::Fn(..)); let mut found_attr: Option<&'a ast::Attribute> = None; diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index f76bbd8381..28efd483c8 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -5,7 +5,8 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; use rustc_expand::base::{self, *}; use rustc_expand::module::DirectoryOwnership; -use rustc_parse::{self, new_parser_from_file, parser::Parser}; +use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::{self, new_parser_from_file}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; use rustc_span::symbol::Symbol; use rustc_span::{self, Pos, Span}; @@ -139,7 +140,7 @@ pub fn expand_include<'cx>( fn make_items(mut self: Box>) -> Option; 1]>> { let mut ret = SmallVec::new(); while self.p.token != token::Eof { - match self.p.parse_item() { + match self.p.parse_item(ForceCollect::No) { Err(mut err) => { err.emit(); break; diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 25d3f46da6..e845f9ec55 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -425,7 +425,7 @@ fn test_type(cx: &ExtCtxt<'_>) -> TestType { fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool { let has_should_panic_attr = cx.sess.contains_name(&i.attrs, sym::should_panic); let sd = &cx.sess.parse_sess.span_diagnostic; - if let ast::ItemKind::Fn(_, ref sig, ref generics, _) = i.kind { + if let ast::ItemKind::Fn(box ast::FnKind(_, ref sig, ref generics, _)) = i.kind { if let ast::Unsafe::Yes(span) = sig.header.unsafety { sd.struct_span_err(i.span, "unsafe functions cannot be used for tests") .span_label(span, "`unsafe` because of this") @@ -474,7 +474,7 @@ fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool { } fn has_bench_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool { - let has_sig = if let ast::ItemKind::Fn(_, ref sig, _, _) = i.kind { + let has_sig = if let ast::ItemKind::Fn(box ast::FnKind(_, ref sig, _, _)) = i.kind { // N.B., inadequate check, but we're running // well before resolve, can't get too deep. sig.decl.inputs.len() == 1 diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 9976140d6b..4ac22be3c2 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -311,7 +311,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { let decl = ecx.fn_decl(vec![], ast::FnRetTy::Ty(main_ret_ty)); let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp }; let def = ast::Defaultness::Final; - let main = ast::ItemKind::Fn(def, sig, ast::Generics::default(), Some(main_body)); + let main = + ast::ItemKind::Fn(box ast::FnKind(def, sig, ast::Generics::default(), Some(main_body))); // Honor the reexport_test_harness_main attribute let main_id = match cx.reexport_test_harness_main { diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index e6d3375fb1..20c58423a0 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -12,6 +12,9 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] + env: + - BACKEND: "" + - BACKEND: --oldbe steps: - uses: actions/checkout@v2 @@ -51,7 +54,7 @@ jobs: export COMPILE_RUNS=2 export RUN_RUNS=2 - ./test.sh + ./test.sh $BACKEND - name: Package prebuilt cg_clif run: tar cvfJ cg_clif.tar.xz build diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json index 04ab5085c1..19ea41563d 100644 --- a/compiler/rustc_codegen_cranelift/.vscode/settings.json +++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json @@ -1,6 +1,7 @@ { // source for rustc_* is not included in the rust-src component; disable the errors about this "rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate"], + "rust-analyzer.assist.importMergeBehavior": "last", "rust-analyzer.cargo.loadOutDirsFromCheck": true, "rust-analyzer.linkedProjects": [ "./Cargo.toml", diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index 67ed41e765..5495cfa5ea 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "anyhow" -version = "1.0.34" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" [[package]] name = "ar" @@ -25,15 +25,15 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" [[package]] name = "cc" -version = "1.0.62" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[package]] name = "cfg-if" @@ -49,16 +49,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +version = "0.69.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +version = "0.69.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" dependencies = [ "byteorder", "cranelift-bforest", @@ -75,8 +75,8 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +version = "0.69.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" dependencies = [ "cranelift-codegen-shared", "cranelift-entity", @@ -84,18 +84,18 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +version = "0.69.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" [[package]] name = "cranelift-entity" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +version = "0.69.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" [[package]] name = "cranelift-frontend" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +version = "0.69.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" dependencies = [ "cranelift-codegen", "log", @@ -103,10 +103,28 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cranelift-jit" +version = "0.69.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-module", + "cranelift-native", + "errno", + "libc", + "log", + "region", + "target-lexicon", + "winapi", +] + [[package]] name = "cranelift-module" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +version = "0.69.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" dependencies = [ "anyhow", "cranelift-codegen", @@ -117,8 +135,8 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +version = "0.69.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" dependencies = [ "cranelift-codegen", "raw-cpuid", @@ -127,8 +145,8 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" +version = "0.69.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#986b5768f9e68f1564b43f32b8a4080a6582c8ca" dependencies = [ "anyhow", "cranelift-codegen", @@ -138,23 +156,6 @@ dependencies = [ "target-lexicon", ] -[[package]] -name = "cranelift-simplejit" -version = "0.68.0" -source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#19640367dbf0da7093e61add3306c8d092644fb3" -dependencies = [ - "cranelift-codegen", - "cranelift-entity", - "cranelift-module", - "cranelift-native", - "errno", - "libc", - "log", - "region", - "target-lexicon", - "winapi", -] - [[package]] name = "crc32fast" version = "1.2.1" @@ -208,9 +209,9 @@ checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] name = "indexmap" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" dependencies = [ "autocfg", "hashbrown", @@ -218,15 +219,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" [[package]] name = "libloading" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1090080fe06ec2648d0da3881d9453d97e71a45f00eb179af7fdd7e3f686fdb0" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" dependencies = [ "cfg-if 1.0.0", "winapi", @@ -234,9 +235,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" dependencies = [ "cfg-if 0.1.10", ] @@ -271,9 +272,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" dependencies = [ "proc-macro2", ] @@ -325,13 +326,14 @@ dependencies = [ "ar", "cranelift-codegen", "cranelift-frontend", + "cranelift-jit", "cranelift-module", "cranelift-object", - "cranelift-simplejit", "gimli", "indexmap", "libloading", "object", + "smallvec", "target-lexicon", ] @@ -361,15 +363,15 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "smallvec" -version = "1.4.2" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "syn" -version = "1.0.48" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" +checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" dependencies = [ "proc-macro2", "quote", @@ -384,18 +386,18 @@ checksum = "4ee5a98e506fb7231a304c3a1bd7c132a55016cf65001e0282480665870dfcb9" [[package]] name = "thiserror" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" +checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" +checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" dependencies = [ "proc-macro2", "quote", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index cbff06749d..3820fce6d1 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -9,10 +9,10 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", features = ["unwind"] } +cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", features = ["unwind", "x86", "x64"] } cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } -cranelift-simplejit = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", optional = true } +cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", optional = true } cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } target-lexicon = "0.11.0" gimli = { version = "0.23.0", default-features = false, features = ["write"]} @@ -21,13 +21,14 @@ object = { version = "0.22.0", default-features = false, features = ["std", "rea ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" } indexmap = "1.0.2" libloading = { version = "0.6.0", optional = true } +smallvec = "1.6.1" # Uncomment to use local checkout of cranelift #[patch."https://github.com/bytecodealliance/wasmtime/"] #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } #cranelift-frontend = { path = "../wasmtime/cranelift/frontend" } #cranelift-module = { path = "../wasmtime/cranelift/module" } -#cranelift-simplejit = { path = "../wasmtime/cranelift/simplejit" } +#cranelift-jit = { path = "../wasmtime/cranelift/jit" } #cranelift-object = { path = "../wasmtime/cranelift/object" } #[patch.crates-io] @@ -35,8 +36,9 @@ libloading = { version = "0.6.0", optional = true } [features] default = ["jit", "inline_asm"] -jit = ["cranelift-simplejit", "libloading"] +jit = ["cranelift-jit", "libloading"] inline_asm = [] +oldbe = [] [profile.dev] # By compiling dependencies with optimizations, performing tests gets much faster. diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index de54bf67f4..6fa5eebdc2 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -1,8 +1,6 @@ -# WIP Cranelift codegen backend for rust +# Cranelift codegen backend for rust -> ⚠⚠⚠ Certain kinds of FFI don't work yet. ⚠⚠⚠ - -The goal of this project is to create an alternative codegen backend for the rust compiler based on [Cranelift](https://github.com/bytecodealliance/wasmtime/blob/master/cranelift). +The goal of this project is to create an alternative codegen backend for the rust compiler based on [Cranelift](https://github.com/bytecodealliance/wasmtime/blob/main/cranelift). This has the potential to improve compilation times in debug mode. If your project doesn't use any of the things listed under "Not yet supported", it should work fine. If not please open an issue. @@ -68,7 +66,15 @@ $ $cg_clif_dir/build/cargo.sh jit or ```bash -$ $cg_clif_dir/build/bin/cg_clif --jit my_crate.rs +$ $cg_clif_dir/build/bin/cg_clif -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs +``` + +There is also an experimental lazy jit mode. In this mode functions are only compiled once they are +first called. It currently does not work with multi-threaded programs. When a not yet compiled +function is called from another thread than the main thread, you will get an ICE. + +```bash +$ $cg_clif_dir/build/cargo.sh lazy-jit ``` ### Shell @@ -77,7 +83,7 @@ These are a few functions that allow you to easily run rust code from the shell ```bash function jit_naked() { - echo "$@" | $cg_clif_dir/build/bin/cg_clif - --jit + echo "$@" | $cg_clif_dir/build/bin/cg_clif - -Cllvm-args=mode=jit -Cprefer-dynamic } function jit() { @@ -95,8 +101,7 @@ function jit_calc() { ## Not yet supported -* Good non-rust abi support ([several problems](https://github.com/bjorn3/rustc_codegen_cranelift/issues/10)) -* Inline assembly ([no cranelift support](https://github.com/bytecodealliance/wasmtime/issues/1041) +* Inline assembly ([no cranelift support](https://github.com/bytecodealliance/wasmtime/issues/1041)) * On Linux there is support for invoking an external assembler for `global_asm!` and `asm!`. `llvm_asm!` will remain unimplemented forever. `asm!` doesn't yet support reg classes. You have to specify specific registers instead. diff --git a/compiler/rustc_codegen_cranelift/build.sh b/compiler/rustc_codegen_cranelift/build.sh index 26041b59cc..598ce35ece 100755 --- a/compiler/rustc_codegen_cranelift/build.sh +++ b/compiler/rustc_codegen_cranelift/build.sh @@ -3,23 +3,29 @@ set -e # Settings export CHANNEL="release" -build_sysroot=1 +build_sysroot="clif" target_dir='build' +oldbe='' while [[ $# != 0 ]]; do case $1 in "--debug") export CHANNEL="debug" ;; - "--without-sysroot") - build_sysroot=0 + "--sysroot") + build_sysroot=$2 + shift ;; "--target-dir") target_dir=$2 shift ;; + "--oldbe") + oldbe='--features oldbe' + ;; *) echo "Unknown flag '$1'" - echo "Usage: ./build.sh [--debug] [--without-sysroot] [--target-dir DIR]" + echo "Usage: ./build.sh [--debug] [--sysroot none|clif|llvm] [--target-dir DIR] [--oldbe]" + exit 1 ;; esac shift @@ -27,23 +33,24 @@ done # Build cg_clif unset CARGO_TARGET_DIR -export RUSTFLAGS="-Zrun_dsymutil=no" unamestr=$(uname) if [[ "$unamestr" == 'Linux' ]]; then export RUSTFLAGS='-Clink-arg=-Wl,-rpath=$ORIGIN/../lib '$RUSTFLAGS elif [[ "$unamestr" == 'Darwin' ]]; then - export RUSTFLAGS='-Clink-arg=-Wl,-rpath,@loader_path/../lib -Zosx-rpath-install-name '$RUSTFLAGS + export RUSTFLAGS='-Csplit-debuginfo=unpacked -Clink-arg=-Wl,-rpath,@loader_path/../lib -Zosx-rpath-install-name '$RUSTFLAGS dylib_ext='dylib' else echo "Unsupported os" exit 1 fi if [[ "$CHANNEL" == "release" ]]; then - cargo build --release + cargo build $oldbe --release else - cargo build + cargo build $oldbe fi +source scripts/ext_config.sh + rm -rf "$target_dir" mkdir "$target_dir" mkdir "$target_dir"/bin "$target_dir"/lib @@ -51,10 +58,29 @@ ln target/$CHANNEL/cg_clif{,_build_sysroot} "$target_dir"/bin ln target/$CHANNEL/*rustc_codegen_cranelift* "$target_dir"/lib ln rust-toolchain scripts/config.sh scripts/cargo.sh "$target_dir" -if [[ "$build_sysroot" == "1" ]]; then - echo "[BUILD] sysroot" - export CG_CLIF_INCR_CACHE_DISABLED=1 - dir=$(pwd) - cd "$target_dir" - time "$dir/build_sysroot/build_sysroot.sh" +mkdir -p "$target_dir/lib/rustlib/$TARGET_TRIPLE/lib/" +if [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then + cp $(rustc --print sysroot)/lib/rustlib/$TARGET_TRIPLE/lib/*.o "$target_dir/lib/rustlib/$TARGET_TRIPLE/lib/" fi + +case "$build_sysroot" in + "none") + ;; + "llvm") + cp -r $(rustc --print sysroot)/lib/rustlib/$TARGET_TRIPLE/lib "$target_dir/lib/rustlib/$TARGET_TRIPLE/" + ;; + "clif") + echo "[BUILD] sysroot" + dir=$(pwd) + cd "$target_dir" + time "$dir/build_sysroot/build_sysroot.sh" + cp lib/rustlib/*/lib/libstd-* lib/ + ;; + *) + echo "Unknown sysroot kind \`$build_sysroot\`." + echo "The allowed values are:" + echo " none A sysroot that doesn't contain the standard library" + echo " llvm Copy the sysroot from rustc compiled by cg_llvm" + echo " clif Build a new sysroot using cg_clif" + exit 1 +esac diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock index a2b8f449f0..0da9999c17 100644 --- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "addr2line" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" dependencies = [ "compiler_builtins", "gimli", @@ -47,9 +47,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "cc" -version = "1.0.65" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95752358c8f7552394baf48cd82695b345628ad3f170d607de3ca03b8dacca15" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[package]] name = "cfg-if" @@ -63,9 +63,7 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369" +version = "0.1.39" dependencies = [ "rustc-std-workspace-core", ] @@ -130,9 +128,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ "compiler_builtins", "libc", @@ -141,9 +139,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.80" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" +checksum = "1cca32fa0182e8c0989459524dc356b8f2b5c10f1b9eb521b7d182c03cf8c5ff" dependencies = [ "rustc-std-workspace-core", ] diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml index e562dedb53..82516c98af 100644 --- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml @@ -5,17 +5,19 @@ 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" } alloc_system = { path = "./alloc_system" } +compiler_builtins = { version = "0.1.39", default-features = false, features = ["no-asm"] } + [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" } +compiler_builtins = { path = "./compiler-builtins" } [profile.dev] lto = "off" diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh b/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh index d7a72df2eb..282ce4a582 100755 --- a/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh +++ b/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh @@ -24,17 +24,16 @@ export CARGO_TARGET_DIR=target # Build libs export RUSTFLAGS="$RUSTFLAGS -Zforce-unstable-if-unmarked -Cpanic=abort" +export __CARGO_DEFAULT_LIB_METADATA="cg_clif" if [[ "$1" != "--debug" ]]; then sysroot_channel='release' # FIXME Enable incremental again once rust-lang/rust#74946 is fixed - # FIXME Enable -Zmir-opt-level=2 again once it doesn't ice anymore - CARGO_INCREMENTAL=0 RUSTFLAGS="$RUSTFLAGS" cargo build --target "$TARGET_TRIPLE" --release + CARGO_INCREMENTAL=0 RUSTFLAGS="$RUSTFLAGS -Zmir-opt-level=2" cargo build --target "$TARGET_TRIPLE" --release else sysroot_channel='debug' cargo build --target "$TARGET_TRIPLE" fi # Copy files to sysroot -mkdir -p "$dir/lib/rustlib/$TARGET_TRIPLE/lib/" ln "target/$TARGET_TRIPLE/$sysroot_channel/deps/"* "$dir/lib/rustlib/$TARGET_TRIPLE/lib/" rm "$dir/lib/rustlib/$TARGET_TRIPLE/lib/"*.{rmeta,d} diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh b/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh index 40fbaf646a..d3b87e02ba 100755 --- a/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh +++ b/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh @@ -29,4 +29,11 @@ git commit --no-gpg-sign -m "Patch $file" done popd -echo "Successfully prepared libcore for building" +git clone https://github.com/rust-lang/compiler-builtins.git || echo "rust-lang/compiler-builtins has already been cloned" +pushd compiler-builtins +git checkout -- . +git checkout 0.1.39 +git apply ../../crate_patches/0001-compiler-builtins-Remove-rotate_left-from-Int.patch +popd + +echo "Successfully prepared sysroot source for building" diff --git a/compiler/rustc_codegen_cranelift/clean_all.sh b/compiler/rustc_codegen_cranelift/clean_all.sh index 5a69c862d0..b47efe72bc 100755 --- a/compiler/rustc_codegen_cranelift/clean_all.sh +++ b/compiler/rustc_codegen_cranelift/clean_all.sh @@ -1,5 +1,5 @@ #!/bin/bash --verbose set -e -rm -rf target/ build/ build_sysroot/{sysroot_src/,target/} perf.data{,.old} +rm -rf target/ build/ build_sysroot/{sysroot_src/,target/,compiler-builtins/} perf.data{,.old} rm -rf rand/ regex/ simple-raytracer/ diff --git a/compiler/rustc_codegen_cranelift/crate_patches/0001-compiler-builtins-Remove-rotate_left-from-Int.patch b/compiler/rustc_codegen_cranelift/crate_patches/0001-compiler-builtins-Remove-rotate_left-from-Int.patch new file mode 100644 index 0000000000..e14768910a --- /dev/null +++ b/compiler/rustc_codegen_cranelift/crate_patches/0001-compiler-builtins-Remove-rotate_left-from-Int.patch @@ -0,0 +1,35 @@ +From 7078cca3cb614e1e82da428380b4e16fc3afef46 Mon Sep 17 00:00:00 2001 +From: bjorn3 +Date: Thu, 21 Jan 2021 14:46:36 +0100 +Subject: [PATCH] Remove rotate_left from Int + +--- + src/int/mod.rs | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/src/int/mod.rs b/src/int/mod.rs +index 06054c8..3bea17b 100644 +--- a/src/int/mod.rs ++++ b/src/int/mod.rs +@@ -85,7 +85,6 @@ pub trait Int: + fn wrapping_sub(self, other: Self) -> Self; + fn wrapping_shl(self, other: u32) -> Self; + fn wrapping_shr(self, other: u32) -> Self; +- fn rotate_left(self, other: u32) -> Self; + fn overflowing_add(self, other: Self) -> (Self, bool); + fn aborting_div(self, other: Self) -> Self; + fn aborting_rem(self, other: Self) -> Self; +@@ -209,10 +208,6 @@ macro_rules! int_impl_common { + ::wrapping_shr(self, other) + } + +- fn rotate_left(self, other: u32) -> Self { +- ::rotate_left(self, other) +- } +- + fn overflowing_add(self, other: Self) -> (Self, bool) { + ::overflowing_add(self, other) + } +-- +2.26.2.7.g19db9cfb68 + diff --git a/compiler/rustc_codegen_cranelift/example/alloc_example.rs b/compiler/rustc_codegen_cranelift/example/alloc_example.rs index dc2ad4c676..f59600ebb3 100644 --- a/compiler/rustc_codegen_cranelift/example/alloc_example.rs +++ b/compiler/rustc_codegen_cranelift/example/alloc_example.rs @@ -11,7 +11,8 @@ use alloc_system::System; #[global_allocator] static ALLOC: System = System; -#[link(name = "c")] +#[cfg_attr(unix, link(name = "c"))] +#[cfg_attr(target_env = "msvc", link(name = "msvcrt"))] extern "C" { fn puts(s: *const u8) -> i32; } diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 10cba99205..002ec7e2e3 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -532,8 +532,8 @@ pub mod intrinsics { } pub mod libc { - #[cfg_attr(not(windows), link(name = "c"))] - #[cfg_attr(windows, link(name = "msvcrt"))] + #[cfg_attr(unix, link(name = "c"))] + #[cfg_attr(target_env = "msvc", link(name = "msvcrt"))] extern "C" { pub fn puts(s: *const i8) -> i32; pub fn printf(format: *const i8, ...) -> i32; diff --git a/compiler/rustc_codegen_cranelift/example/mod_bench.rs b/compiler/rustc_codegen_cranelift/example/mod_bench.rs index bc65221362..152041aa9e 100644 --- a/compiler/rustc_codegen_cranelift/example/mod_bench.rs +++ b/compiler/rustc_codegen_cranelift/example/mod_bench.rs @@ -1,7 +1,8 @@ #![feature(start, box_syntax, core_intrinsics, lang_items)] #![no_std] -#[link(name = "c")] +#[cfg_attr(unix, link(name = "c"))] +#[cfg_attr(target_env = "msvc", link(name = "msvcrt"))] extern {} #[panic_handler] diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index b38e25328a..015bbdfed4 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -15,6 +15,8 @@ fn main() { let stderr = ::std::io::stderr(); let mut stderr = stderr.lock(); + // FIXME support lazy jit when multi threading + #[cfg(not(lazy_jit))] std::thread::spawn(move || { println!("Hello from another thread!"); }); diff --git a/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch index 8cfffe580a..3eb10069ad 100644 --- a/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch +++ b/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch @@ -119,5 +119,21 @@ index 6609bc3..241b497 100644 #[test] #[should_panic(expected = "index 0 greater than length of slice")] +diff --git a/library/core/tests/num/ops.rs b/library/core/tests/num/ops.rs +index 9979cc8..d5d1d83 100644 +--- a/library/core/tests/num/ops.rs ++++ b/library/core/tests/num/ops.rs +@@ -238,7 +238,7 @@ macro_rules! test_shift_assign { + } + }; + } +-test_shift!(test_shl_defined, Shl::shl); +-test_shift_assign!(test_shl_assign_defined, ShlAssign::shl_assign); +-test_shift!(test_shr_defined, Shr::shr); +-test_shift_assign!(test_shr_assign_defined, ShrAssign::shr_assign); ++//test_shift!(test_shl_defined, Shl::shl); ++//test_shift_assign!(test_shl_assign_defined, ShlAssign::shl_assign); ++//test_shift!(test_shr_defined, Shr::shr); ++//test_shift_assign!(test_shr_assign_defined, ShrAssign::shr_assign); -- 2.21.0 (Apple Git-122) diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index ed1e64f45d..a08f00d19c 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1 +1 @@ -nightly-2020-11-27 +nightly-2021-01-30 diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo.sh b/compiler/rustc_codegen_cranelift/scripts/cargo.sh index dcd40acc02..a3d6d30305 100755 --- a/compiler/rustc_codegen_cranelift/scripts/cargo.sh +++ b/compiler/rustc_codegen_cranelift/scripts/cargo.sh @@ -10,7 +10,9 @@ cmd=$1 shift || true if [[ "$cmd" = "jit" ]]; then -cargo "+${TOOLCHAIN}" rustc "$@" -- --jit +cargo "+${TOOLCHAIN}" rustc "$@" -- -Cllvm-args=mode=jit -Cprefer-dynamic +elif [[ "$cmd" = "lazy-jit" ]]; then +cargo "+${TOOLCHAIN}" rustc "$@" -- -Cllvm-args=mode=jit-lazy -Cprefer-dynamic else cargo "+${TOOLCHAIN}" "$cmd" "$@" fi diff --git a/compiler/rustc_codegen_cranelift/scripts/config.sh b/compiler/rustc_codegen_cranelift/scripts/config.sh index dea037e2bc..834708aa9a 100644 --- a/compiler/rustc_codegen_cranelift/scripts/config.sh +++ b/compiler/rustc_codegen_cranelift/scripts/config.sh @@ -12,28 +12,6 @@ else exit 1 fi -HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ") -TARGET_TRIPLE=$HOST_TRIPLE -#TARGET_TRIPLE="x86_64-pc-windows-gnu" -#TARGET_TRIPLE="aarch64-unknown-linux-gnu" - -linker='' -RUN_WRAPPER='' -export JIT_SUPPORTED=1 -if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then - export JIT_SUPPORTED=0 - if [[ "$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' - elif [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then - # We are cross-compiling for Windows. Run tests in wine. - RUN_WRAPPER='wine' - else - echo "Unknown non-native platform" - fi -fi - if echo "$RUSTC_WRAPPER" | grep sccache; then echo echo -e "\x1b[1;93m=== Warning: Unset RUSTC_WRAPPER to prevent interference with sccache ===\x1b[0m" @@ -44,16 +22,14 @@ fi dir=$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd) export RUSTC=$dir"/bin/cg_clif" -export RUSTFLAGS=$linker" "$RUSTFLAGS + export RUSTDOCFLAGS=$linker' -Cpanic=abort -Zpanic-abort-tests '\ '-Zcodegen-backend='$dir'/lib/librustc_codegen_cranelift.'$dylib_ext' --sysroot '$dir # FIXME remove once the atomic shim is gone -if [[ $(uname) == 'Darwin' ]]; then +if [[ "$unamestr" == 'Darwin' ]]; then export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup" fi -export LD_LIBRARY_PATH="$(rustc --print sysroot)/lib" +export LD_LIBRARY_PATH="$(rustc --print sysroot)/lib:"$dir"/lib" export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH - -export CG_CLIF_DISPLAY_CG_TIME=1 diff --git a/compiler/rustc_codegen_cranelift/scripts/ext_config.sh b/compiler/rustc_codegen_cranelift/scripts/ext_config.sh new file mode 100644 index 0000000000..7971f620df --- /dev/null +++ b/compiler/rustc_codegen_cranelift/scripts/ext_config.sh @@ -0,0 +1,27 @@ +# Note to people running shellcheck: this file should only be sourced, not executed directly. + +# Various env vars that should only be set for the build system but not for cargo.sh + +set -e + +export CG_CLIF_DISPLAY_CG_TIME=1 +export CG_CLIF_INCR_CACHE_DISABLED=1 + +export HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ") +export TARGET_TRIPLE=${TARGET_TRIPLE:-$HOST_TRIPLE} + +export RUN_WRAPPER='' +export JIT_SUPPORTED=1 +if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then + export JIT_SUPPORTED=0 + if [[ "$TARGET_TRIPLE" == "aarch64-unknown-linux-gnu" ]]; then + # We are cross-compiling for aarch64. Use the correct linker and run tests in qemu. + export RUSTFLAGS='-Clinker=aarch64-linux-gnu-gcc '$RUSTFLAGS + export RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu' + elif [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then + # We are cross-compiling for Windows. Run tests in wine. + export RUN_WRAPPER='wine' + else + echo "Unknown non-native platform" + fi +fi diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs index 3327c10089..15388926ec 100755 --- a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs +++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs @@ -4,7 +4,7 @@ pushd $(dirname "$0")/../ source build/config.sh popd -PROFILE=$1 OUTPUT=$2 exec $RUSTC $RUSTFLAGS --jit $0 +PROFILE=$1 OUTPUT=$2 exec $RUSTC $RUSTFLAGS -Cllvm-args=mode=jit -Cprefer-dynamic $0 #*/ //! This program filters away uninteresting samples and trims uninteresting frames for stackcollapse diff --git a/compiler/rustc_codegen_cranelift/scripts/tests.sh b/compiler/rustc_codegen_cranelift/scripts/tests.sh index 114b6f30a4..d37b57babe 100755 --- a/compiler/rustc_codegen_cranelift/scripts/tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/tests.sh @@ -3,7 +3,7 @@ set -e source build/config.sh -export CG_CLIF_INCR_CACHE_DISABLED=1 +source scripts/ext_config.sh MY_RUSTC="$RUSTC $RUSTFLAGS -L crate=target/out --out-dir target/out -Cdebuginfo=2" function no_sysroot_tests() { @@ -15,7 +15,10 @@ function no_sysroot_tests() { if [[ "$JIT_SUPPORTED" = "1" ]]; then echo "[JIT] mini_core_hello_world" - CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC --jit example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" + CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" + + echo "[JIT-lazy] mini_core_hello_world" + CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" else echo "[JIT] mini_core_hello_world (skipped)" fi @@ -37,7 +40,10 @@ function base_sysroot_tests() { if [[ "$JIT_SUPPORTED" = "1" ]]; then echo "[JIT] std_example" - $MY_RUSTC --jit example/std_example.rs --target "$HOST_TRIPLE" + $MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE" + + echo "[JIT-lazy] std_example" + $MY_RUSTC -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/std_example.rs --cfg lazy_jit --target "$HOST_TRIPLE" else echo "[JIT] std_example (skipped)" fi diff --git a/compiler/rustc_codegen_cranelift/src/abi/comments.rs b/compiler/rustc_codegen_cranelift/src/abi/comments.rs index 01073d26e8..9aab45b62e 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/comments.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/comments.rs @@ -4,10 +4,10 @@ use std::borrow::Cow; use rustc_middle::mir; +use rustc_target::abi::call::PassMode; use cranelift_codegen::entity::EntityRef; -use crate::abi::pass_mode::*; use crate::prelude::*; pub(super) fn add_args_header_comment(fx: &mut FunctionCx<'_, '_, impl Module>) { @@ -21,9 +21,9 @@ pub(super) fn add_arg_comment<'tcx>( kind: &str, local: Option, local_field: Option, - params: EmptySinglePair, - pass_mode: PassMode, - ty: Ty<'tcx>, + params: &[Value], + arg_abi_mode: PassMode, + arg_layout: TyAndLayout<'tcx>, ) { let local = if let Some(local) = local { Cow::Owned(format!("{:?}", local)) @@ -37,12 +37,20 @@ pub(super) fn add_arg_comment<'tcx>( }; let params = match params { - Empty => Cow::Borrowed("-"), - Single(param) => Cow::Owned(format!("= {:?}", param)), - Pair(param_a, param_b) => Cow::Owned(format!("= {:?}, {:?}", param_a, param_b)), + [] => Cow::Borrowed("-"), + [param] => Cow::Owned(format!("= {:?}", param)), + [param_a, param_b] => Cow::Owned(format!("= {:?},{:?}", param_a, param_b)), + params => Cow::Owned(format!( + "= {}", + params + .iter() + .map(ToString::to_string) + .collect::>() + .join(",") + )), }; - let pass_mode = format!("{:?}", pass_mode); + let pass_mode = format!("{:?}", arg_abi_mode); fx.add_global_comment(format!( "{kind:5}{local:>3}{local_field:<5} {params:10} {pass_mode:36} {ty:?}", kind = kind, @@ -50,7 +58,7 @@ pub(super) fn add_arg_comment<'tcx>( local_field = local_field, params = params, pass_mode = pass_mode, - ty = ty, + ty = arg_layout.ty, )); } diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 76e1987459..b2647e6c8d 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -6,199 +6,51 @@ mod pass_mode; mod returning; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::ty::layout::FnAbiExt; +use rustc_target::abi::call::{Conv, FnAbi}; use rustc_target::spec::abi::Abi; -use cranelift_codegen::ir::{AbiParam, ArgumentPurpose}; +use cranelift_codegen::ir::AbiParam; +use smallvec::smallvec; use self::pass_mode::*; use crate::prelude::*; pub(crate) use self::returning::{can_return_to_ssa_var, codegen_return}; -// Copied from https://github.com/rust-lang/rust/blob/f52c72948aa1dd718cc1f168d21c91c584c0a662/src/librustc_middle/ty/layout.rs#L2301 -#[rustfmt::skip] -pub(crate) fn fn_sig_for_fn_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::PolyFnSig<'tcx> { - use rustc_middle::ty::subst::Subst; - - // FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function. - let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - match *ty.kind() { - ty::FnDef(..) => { - // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering - // parameters unused if they show up in the signature, but not in the `mir::Body` - // (i.e. due to being inside a projection that got normalized, see - // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping - // track of a polymorphization `ParamEnv` to allow normalizing later. - let mut sig = match *ty.kind() { - ty::FnDef(def_id, substs) => tcx - .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id)) - .subst(tcx, substs), - _ => unreachable!(), - }; - - if let ty::InstanceDef::VtableShim(..) = instance.def { - // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. - sig = sig.map_bound(|mut sig| { - let mut inputs_and_output = sig.inputs_and_output.to_vec(); - inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]); - sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output); - sig - }); - } - sig - } - ty::Closure(def_id, substs) => { - let sig = substs.as_closure().sig(); - - let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); - sig.map_bound(|sig| { - tcx.mk_fn_sig( - std::iter::once(env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.c_variadic, - sig.unsafety, - sig.abi, - ) - }) - } - ty::Generator(_, substs, _) => { - let sig = substs.as_generator().poly_sig(); - - let env_region = ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind: ty::BrEnv }); - let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); - - let pin_did = tcx.require_lang_item(rustc_hir::LangItem::Pin, None); - let pin_adt_ref = tcx.adt_def(pin_did); - let pin_substs = tcx.intern_substs(&[env_ty.into()]); - let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs); - - sig.map_bound(|sig| { - let state_did = tcx.require_lang_item(rustc_hir::LangItem::GeneratorState, None); - let state_adt_ref = tcx.adt_def(state_did); - let state_substs = - tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]); - let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); - - tcx.mk_fn_sig( - [env_ty, sig.resume_ty].iter(), - &ret_ty, - false, - rustc_hir::Unsafety::Normal, - rustc_target::spec::abi::Abi::Rust, - ) - }) - } - _ => bug!("unexpected type {:?} in Instance::fn_sig", ty), - } -} - -fn clif_sig_from_fn_sig<'tcx>( +fn clif_sig_from_fn_abi<'tcx>( tcx: TyCtxt<'tcx>, triple: &target_lexicon::Triple, - sig: FnSig<'tcx>, - span: Span, - is_vtable_fn: bool, - requires_caller_location: bool, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, ) -> Signature { - let abi = match sig.abi { - Abi::System => Abi::C, - abi => abi, - }; - let (call_conv, inputs, output): (CallConv, Vec>, Ty<'tcx>) = match abi { - Abi::Rust => ( - CallConv::triple_default(triple), - sig.inputs().to_vec(), - sig.output(), - ), - Abi::C | Abi::Unadjusted => ( - CallConv::triple_default(triple), - sig.inputs().to_vec(), - sig.output(), - ), - Abi::SysV64 => (CallConv::SystemV, sig.inputs().to_vec(), sig.output()), - Abi::RustCall => { - assert_eq!(sig.inputs().len(), 2); - let extra_args = match sig.inputs().last().unwrap().kind() { - ty::Tuple(ref tupled_arguments) => tupled_arguments, - _ => bug!("argument to function with \"rust-call\" ABI is not a tuple"), - }; - let mut inputs: Vec> = vec![sig.inputs()[0]]; - inputs.extend(extra_args.types()); - (CallConv::triple_default(triple), inputs, sig.output()) + let call_conv = match fn_abi.conv { + Conv::Rust | Conv::C => CallConv::triple_default(triple), + Conv::X86_64SysV => CallConv::SystemV, + Conv::X86_64Win64 => CallConv::WindowsFastcall, + Conv::ArmAapcs + | Conv::CCmseNonSecureCall + | Conv::Msp430Intr + | Conv::PtxKernel + | Conv::X86Fastcall + | Conv::X86Intr + | Conv::X86Stdcall + | Conv::X86ThisCall + | Conv::X86VectorCall + | Conv::AmdGpuKernel + | Conv::AvrInterrupt + | Conv::AvrNonBlockingInterrupt => { + todo!("{:?}", fn_abi.conv) } - Abi::System => unreachable!(), - Abi::RustIntrinsic => ( - CallConv::triple_default(triple), - sig.inputs().to_vec(), - sig.output(), - ), - _ => unimplemented!("unsupported abi {:?}", sig.abi), }; - - let inputs = inputs - .into_iter() - .enumerate() - .map(|(i, ty)| { - let mut layout = tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap(); - if i == 0 && is_vtable_fn { - // Virtual calls turn their self param into a thin pointer. - // See https://github.com/rust-lang/rust/blob/37b6a5e5e82497caf5353d9d856e4eb5d14cbe06/src/librustc/ty/layout.rs#L2519-L2572 for more info - layout = tcx - .layout_of(ParamEnv::reveal_all().and(tcx.mk_mut_ptr(tcx.mk_unit()))) - .unwrap(); - } - let pass_mode = get_pass_mode(tcx, layout); - if abi != Abi::Rust && abi != Abi::RustCall && abi != Abi::RustIntrinsic { - match pass_mode { - PassMode::NoPass | PassMode::ByVal(_) => {} - PassMode::ByRef { size: Some(size) } => { - let purpose = ArgumentPurpose::StructArgument(u32::try_from(size.bytes()).expect("struct too big to pass on stack")); - return EmptySinglePair::Single(AbiParam::special(pointer_ty(tcx), purpose)).into_iter(); - } - PassMode::ByValPair(_, _) | PassMode::ByRef { size: None } => { - tcx.sess.span_warn( - span, - &format!( - "Argument of type `{:?}` with pass mode `{:?}` is not yet supported \ - for non-rust abi `{}`. Calling this function may result in a crash.", - layout.ty, - pass_mode, - abi, - ), - ); - } - } - } - pass_mode.get_param_ty(tcx).map(AbiParam::new).into_iter() - }) + let inputs = fn_abi + .args + .iter() + .map(|arg_abi| arg_abi.get_abi_param(tcx).into_iter()) .flatten(); - let (mut params, returns): (Vec<_>, Vec<_>) = match get_pass_mode( - tcx, - tcx.layout_of(ParamEnv::reveal_all().and(output)).unwrap(), - ) { - PassMode::NoPass => (inputs.collect(), vec![]), - PassMode::ByVal(ret_ty) => (inputs.collect(), vec![AbiParam::new(ret_ty)]), - PassMode::ByValPair(ret_ty_a, ret_ty_b) => ( - inputs.collect(), - vec![AbiParam::new(ret_ty_a), AbiParam::new(ret_ty_b)], - ), - PassMode::ByRef { size: Some(_) } => { - ( - Some(pointer_ty(tcx)) // First param is place to put return val - .into_iter() - .map(|ty| AbiParam::special(ty, ArgumentPurpose::StructReturn)) - .chain(inputs) - .collect(), - vec![], - ) - } - PassMode::ByRef { size: None } => todo!(), - }; - - if requires_caller_location { - params.push(AbiParam::new(pointer_ty(tcx))); - } + let (return_ptr, returns) = fn_abi.ret.get_abi_return(tcx); + // Sometimes the first param is an pointer to the place where the return value needs to be stored. + let params: Vec<_> = return_ptr.into_iter().chain(inputs).collect(); Signature { params, @@ -207,30 +59,17 @@ fn clif_sig_from_fn_sig<'tcx>( } } -pub(crate) fn get_function_name_and_sig<'tcx>( +pub(crate) fn get_function_sig<'tcx>( tcx: TyCtxt<'tcx>, triple: &target_lexicon::Triple, inst: Instance<'tcx>, - support_vararg: bool, -) -> (String, Signature) { +) -> Signature { assert!(!inst.substs.needs_infer()); - let fn_sig = tcx - .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), fn_sig_for_fn_abi(tcx, inst)); - if fn_sig.c_variadic && !support_vararg { - tcx.sess.span_fatal( - tcx.def_span(inst.def_id()), - "Variadic function definitions are not yet supported", - ); - } - let sig = clif_sig_from_fn_sig( + clif_sig_from_fn_abi( tcx, triple, - fn_sig, - tcx.def_span(inst.def_id()), - false, - inst.def.requires_caller_location(tcx), - ); - (tcx.symbol_name(inst).name.to_string(), sig) + &FnAbi::of_instance(&RevealAllLayoutCx(tcx), inst, &[]), + ) } /// Instance must be monomorphized @@ -239,7 +78,8 @@ pub(crate) fn import_function<'tcx>( module: &mut impl Module, inst: Instance<'tcx>, ) -> FuncId { - let (name, sig) = get_function_name_and_sig(tcx, module.isa().triple(), inst, true); + let name = tcx.symbol_name(inst).name.to_string(); + let sig = get_function_sig(tcx, module.isa().triple(), inst); module .declare_function(&name, Linkage::Import, &sig) .unwrap() @@ -263,13 +103,13 @@ impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { pub(crate) fn lib_call( &mut self, name: &str, - input_tys: Vec, - output_tys: Vec, + params: Vec, + returns: Vec, args: &[Value], ) -> &[Value] { let sig = Signature { - params: input_tys.iter().cloned().map(AbiParam::new).collect(), - returns: output_tys.iter().cloned().map(AbiParam::new).collect(), + params, + returns, call_conv: CallConv::triple_default(self.triple()), }; let func_id = self @@ -301,16 +141,18 @@ impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { .iter() .map(|arg| { ( - self.clif_type(arg.layout().ty).unwrap(), + AbiParam::new(self.clif_type(arg.layout().ty).unwrap()), arg.load_scalar(self), ) }) .unzip(); let return_layout = self.layout_of(return_ty); let return_tys = if let ty::Tuple(tup) = return_ty.kind() { - tup.types().map(|ty| self.clif_type(ty).unwrap()).collect() + tup.types() + .map(|ty| AbiParam::new(self.clif_type(ty).unwrap())) + .collect() } else { - vec![self.clif_type(return_ty).unwrap()] + vec![AbiParam::new(self.clif_type(return_ty).unwrap())] }; let ret_vals = self.lib_call(name, input_tys, return_tys, &args); match *ret_vals { @@ -352,12 +194,25 @@ pub(crate) fn codegen_fn_prelude<'tcx>( fx: &mut FunctionCx<'_, 'tcx, impl Module>, start_block: Block, ) { + fx.bcx.append_block_params_for_function_params(start_block); + + fx.bcx.switch_to_block(start_block); + fx.bcx.ins().nop(); + let ssa_analyzed = crate::analyze::analyze(fx); #[cfg(debug_assertions)] self::comments::add_args_header_comment(fx); - let ret_place = self::returning::codegen_return_param(fx, &ssa_analyzed, start_block); + let mut block_params_iter = fx + .bcx + .func + .dfg + .block_params(start_block) + .to_vec() + .into_iter(); + let ret_place = + self::returning::codegen_return_param(fx, &ssa_analyzed, &mut block_params_iter); assert_eq!(fx.local_map.push(ret_place), RETURN_PLACE); // None means pass_mode == NoPass @@ -366,6 +221,9 @@ pub(crate) fn codegen_fn_prelude<'tcx>( Spread(Vec>>), } + let fn_abi = fx.fn_abi.take().unwrap(); + let mut arg_abis_iter = fn_abi.args.iter(); + let func_params = fx .mir .args_iter() @@ -385,14 +243,18 @@ pub(crate) fn codegen_fn_prelude<'tcx>( }; let mut params = Vec::new(); - for (i, arg_ty) in tupled_arg_tys.types().enumerate() { - let param = cvalue_for_param(fx, start_block, Some(local), Some(i), arg_ty); + for (i, _arg_ty) in tupled_arg_tys.types().enumerate() { + let arg_abi = arg_abis_iter.next().unwrap(); + let param = + cvalue_for_param(fx, Some(local), Some(i), arg_abi, &mut block_params_iter); params.push(param); } (local, ArgKind::Spread(params), arg_ty) } else { - let param = cvalue_for_param(fx, start_block, Some(local), None, arg_ty); + let arg_abi = arg_abis_iter.next().unwrap(); + let param = + cvalue_for_param(fx, Some(local), None, arg_abi, &mut block_params_iter); (local, ArgKind::Normal(param), arg_ty) } }) @@ -401,13 +263,14 @@ pub(crate) fn codegen_fn_prelude<'tcx>( assert!(fx.caller_location.is_none()); if fx.instance.def.requires_caller_location(fx.tcx) { // Store caller location for `#[track_caller]`. - fx.caller_location = Some( - cvalue_for_param(fx, start_block, None, None, fx.tcx.caller_location_ty()).unwrap(), - ); + let arg_abi = arg_abis_iter.next().unwrap(); + fx.caller_location = + Some(cvalue_for_param(fx, None, None, arg_abi, &mut block_params_iter).unwrap()); } - fx.bcx.switch_to_block(start_block); - fx.bcx.ins().nop(); + assert!(arg_abis_iter.next().is_none(), "ArgAbi left behind"); + fx.fn_abi = Some(fn_abi); + assert!(block_params_iter.next().is_none(), "arg_value left behind"); #[cfg(debug_assertions)] self::comments::add_locals_header_comment(fx); @@ -533,6 +396,21 @@ pub(crate) fn codegen_terminator_call<'tcx>( None }; + let extra_args = &args[fn_sig.inputs().len()..]; + let extra_args = extra_args + .iter() + .map(|op_arg| fx.monomorphize(op_arg.ty(fx.mir, fx.tcx))) + .collect::>(); + let fn_abi = if let Some(instance) = instance { + FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), instance, &extra_args) + } else { + FnAbi::of_fn_ptr( + &RevealAllLayoutCx(fx.tcx), + fn_ty.fn_sig(fx.tcx), + &extra_args, + ) + }; + let is_cold = instance .map(|inst| { fx.tcx @@ -570,8 +448,8 @@ pub(crate) fn codegen_terminator_call<'tcx>( // | indirect call target // | | the first argument to be passed - // v v v virtual calls are special cased below - let (func_ref, first_arg, is_virtual_call) = match instance { + // v v + let (func_ref, first_arg) = match instance { // Trait object call Some(Instance { def: InstanceDef::Virtual(_, idx), @@ -582,23 +460,19 @@ pub(crate) fn codegen_terminator_call<'tcx>( let nop_inst = fx.bcx.ins().nop(); fx.add_comment( nop_inst, - format!( - "virtual call; self arg pass mode: {:?}", - get_pass_mode(fx.tcx, args[0].layout()) - ), + format!("virtual call; self arg pass mode: {:?}", &fn_abi.args[0],), ); } let (ptr, method) = crate::vtable::get_ptr_and_method_ref(fx, args[0], idx); - (Some(method), Single(ptr), true) + (Some(method), smallvec![ptr]) } // Normal call Some(_) => ( None, args.get(0) - .map(|arg| adjust_arg_for_abi(fx, *arg)) - .unwrap_or(Empty), - false, + .map(|arg| adjust_arg_for_abi(fx, *arg, &fn_abi.args[0])) + .unwrap_or(smallvec![]), ), // Indirect call @@ -612,23 +486,27 @@ pub(crate) fn codegen_terminator_call<'tcx>( ( Some(func), args.get(0) - .map(|arg| adjust_arg_for_abi(fx, *arg)) - .unwrap_or(Empty), - false, + .map(|arg| adjust_arg_for_abi(fx, *arg, &fn_abi.args[0])) + .unwrap_or(smallvec![]), ) } }; let ret_place = destination.map(|(place, _)| place); - let (call_inst, call_args) = - self::returning::codegen_with_call_return_arg(fx, fn_sig, ret_place, |fx, return_ptr| { + let (call_inst, call_args) = self::returning::codegen_with_call_return_arg( + fx, + &fn_abi.ret, + ret_place, + |fx, return_ptr| { + let regular_args_count = args.len(); let mut call_args: Vec = return_ptr .into_iter() .chain(first_arg.into_iter()) .chain( args.into_iter() + .enumerate() .skip(1) - .map(|arg| adjust_arg_for_abi(fx, arg).into_iter()) + .map(|(i, arg)| adjust_arg_for_abi(fx, arg, &fn_abi.args[i]).into_iter()) .flatten(), ) .collect::>(); @@ -639,18 +517,17 @@ pub(crate) fn codegen_terminator_call<'tcx>( { // Pass the caller location for `#[track_caller]`. let caller_location = fx.get_caller_location(span); - call_args.extend(adjust_arg_for_abi(fx, caller_location).into_iter()); + call_args.extend( + adjust_arg_for_abi(fx, caller_location, &fn_abi.args[regular_args_count]) + .into_iter(), + ); + assert_eq!(fn_abi.args.len(), regular_args_count + 1); + } else { + assert_eq!(fn_abi.args.len(), regular_args_count); } let call_inst = if let Some(func_ref) = func_ref { - let sig = clif_sig_from_fn_sig( - fx.tcx, - fx.triple(), - fn_sig, - span, - is_virtual_call, - false, // calls through function pointers never pass the caller location - ); + let sig = clif_sig_from_fn_abi(fx.tcx, fx.triple(), &fn_abi); let sig = fx.bcx.import_signature(sig); fx.bcx.ins().call_indirect(sig, func_ref, &call_args) } else { @@ -660,7 +537,8 @@ pub(crate) fn codegen_terminator_call<'tcx>( }; (call_inst, call_args) - }); + }, + ); // FIXME find a cleaner way to support varargs if fn_sig.c_variadic { @@ -701,37 +579,33 @@ pub(crate) fn codegen_drop<'tcx>( drop_place: CPlace<'tcx>, ) { let ty = drop_place.layout().ty; - let drop_fn = Instance::resolve_drop_in_place(fx.tcx, ty).polymorphize(fx.tcx); + let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty).polymorphize(fx.tcx); - if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { + if let ty::InstanceDef::DropGlue(_, None) = drop_instance.def { // we don't actually need to drop anything } else { - let drop_fn_ty = drop_fn.ty(fx.tcx, ParamEnv::reveal_all()); - let fn_sig = fx.tcx.normalize_erasing_late_bound_regions( - ParamEnv::reveal_all(), - drop_fn_ty.fn_sig(fx.tcx), - ); - assert_eq!(fn_sig.output(), fx.tcx.mk_unit()); - match ty.kind() { ty::Dynamic(..) => { let (ptr, vtable) = drop_place.to_ptr_maybe_unsized(); let ptr = ptr.get_addr(fx); let drop_fn = crate::vtable::drop_fn_of_obj(fx, vtable.unwrap()); - let sig = clif_sig_from_fn_sig( - fx.tcx, - fx.triple(), - fn_sig, - span, - true, - false, // `drop_in_place` is never `#[track_caller]` - ); + // FIXME(eddyb) perhaps move some of this logic into + // `Instance::resolve_drop_in_place`? + let virtual_drop = Instance { + def: ty::InstanceDef::Virtual(drop_instance.def_id(), 0), + substs: drop_instance.substs, + }; + let fn_abi = FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), virtual_drop, &[]); + + let sig = clif_sig_from_fn_abi(fx.tcx, fx.triple(), &fn_abi); let sig = fx.bcx.import_signature(sig); fx.bcx.ins().call_indirect(sig, drop_fn, &[ptr]); } _ => { - assert!(!matches!(drop_fn.def, InstanceDef::Virtual(_, _))); + assert!(!matches!(drop_instance.def, InstanceDef::Virtual(_, _))); + + let fn_abi = FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), drop_instance, &[]); let arg_value = drop_place.place_ref( fx, @@ -743,17 +617,19 @@ pub(crate) fn codegen_drop<'tcx>( }, )), ); - let arg_value = adjust_arg_for_abi(fx, arg_value); + let arg_value = adjust_arg_for_abi(fx, arg_value, &fn_abi.args[0]); let mut call_args: Vec = arg_value.into_iter().collect::>(); - if drop_fn.def.requires_caller_location(fx.tcx) { + if drop_instance.def.requires_caller_location(fx.tcx) { // Pass the caller location for `#[track_caller]`. let caller_location = fx.get_caller_location(span); - call_args.extend(adjust_arg_for_abi(fx, caller_location).into_iter()); + call_args.extend( + adjust_arg_for_abi(fx, caller_location, &fn_abi.args[1]).into_iter(), + ); } - let func_ref = fx.get_function_ref(drop_fn); + let func_ref = fx.get_function_ref(drop_instance); fx.bcx.ins().call(func_ref, &call_args); } } diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 8e3682c86c..1202c23dbe 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -1,140 +1,281 @@ //! Argument passing use crate::prelude::*; +use crate::value_and_place::assert_assignable; -pub(super) use EmptySinglePair::*; +use cranelift_codegen::ir::{ArgumentExtension, ArgumentPurpose}; +use rustc_target::abi::call::{ + ArgAbi, ArgAttributes, ArgExtension as RustcArgExtension, CastTarget, PassMode, Reg, RegKind, +}; +use smallvec::{smallvec, SmallVec}; -#[derive(Copy, Clone, Debug)] -pub(super) enum PassMode { - NoPass, - ByVal(Type), - ByValPair(Type, Type), - ByRef { size: Option }, +pub(super) trait ArgAbiExt<'tcx> { + fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]>; + fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option, Vec); } -#[derive(Copy, Clone, Debug)] -pub(super) enum EmptySinglePair { - Empty, - Single(T), - Pair(T, T), +fn reg_to_abi_param(reg: Reg) -> AbiParam { + let clif_ty = match (reg.kind, reg.size.bytes()) { + (RegKind::Integer, 1) => types::I8, + (RegKind::Integer, 2) => types::I16, + (RegKind::Integer, 4) => types::I32, + (RegKind::Integer, 8) => types::I64, + (RegKind::Integer, 16) => types::I128, + (RegKind::Float, 4) => types::F32, + (RegKind::Float, 8) => types::F64, + (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(), + _ => unreachable!("{:?}", reg), + }; + AbiParam::new(clif_ty) } -impl EmptySinglePair { - pub(super) fn into_iter(self) -> EmptySinglePairIter { - EmptySinglePairIter(self) - } - - pub(super) fn map(self, mut f: impl FnMut(T) -> U) -> EmptySinglePair { - match self { - Empty => Empty, - Single(v) => Single(f(v)), - Pair(a, b) => Pair(f(a), f(b)), - } +fn apply_arg_attrs_to_abi_param(mut param: AbiParam, arg_attrs: ArgAttributes) -> AbiParam { + match arg_attrs.arg_ext { + RustcArgExtension::None => {} + RustcArgExtension::Zext => param.extension = ArgumentExtension::Uext, + RustcArgExtension::Sext => param.extension = ArgumentExtension::Sext, } + param } -pub(super) struct EmptySinglePairIter(EmptySinglePair); - -impl Iterator for EmptySinglePairIter { - type Item = T; +fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> { + let (rest_count, rem_bytes) = if cast.rest.unit.size.bytes() == 0 { + (0, 0) + } else { + ( + cast.rest.total.bytes() / cast.rest.unit.size.bytes(), + cast.rest.total.bytes() % cast.rest.unit.size.bytes(), + ) + }; - fn next(&mut self) -> Option { - match std::mem::replace(&mut self.0, Empty) { - Empty => None, - Single(v) => Some(v), - Pair(a, b) => { - self.0 = Single(b); - Some(a) - } + if cast.prefix.iter().all(|x| x.is_none()) { + // Simplify to a single unit when there is no prefix and size <= unit size + if cast.rest.total <= cast.rest.unit.size { + let clif_ty = match (cast.rest.unit.kind, cast.rest.unit.size.bytes()) { + (RegKind::Integer, 1) => types::I8, + (RegKind::Integer, 2) => types::I16, + (RegKind::Integer, 3..=4) => types::I32, + (RegKind::Integer, 5..=8) => types::I64, + (RegKind::Integer, 9..=16) => types::I128, + (RegKind::Float, 4) => types::F32, + (RegKind::Float, 8) => types::F64, + (RegKind::Vector, size) => types::I8.by(u16::try_from(size).unwrap()).unwrap(), + _ => unreachable!("{:?}", cast.rest.unit), + }; + return smallvec![AbiParam::new(clif_ty)]; } } -} -impl EmptySinglePair { - pub(super) fn assert_single(self) -> T { - match self { - Single(v) => v, - _ => panic!("Called assert_single on {:?}", self), - } - } + // Create list of fields in the main structure + let mut args = cast + .prefix + .iter() + .flatten() + .map(|&kind| { + reg_to_abi_param(Reg { + kind, + size: cast.prefix_chunk_size, + }) + }) + .chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit))) + .collect::>(); - pub(super) fn assert_pair(self) -> (T, T) { - match self { - Pair(a, b) => (a, b), - _ => panic!("Called assert_pair on {:?}", self), - } + // Append final integer + if rem_bytes != 0 { + // Only integers can be really split further. + assert_eq!(cast.rest.unit.kind, RegKind::Integer); + args.push(reg_to_abi_param(Reg { + kind: RegKind::Integer, + size: Size::from_bytes(rem_bytes), + })); } -} -impl PassMode { - pub(super) fn get_param_ty(self, tcx: TyCtxt<'_>) -> EmptySinglePair { - match self { - PassMode::NoPass => Empty, - PassMode::ByVal(clif_type) => Single(clif_type), - PassMode::ByValPair(a, b) => Pair(a, b), - PassMode::ByRef { size: Some(_) } => Single(pointer_ty(tcx)), - PassMode::ByRef { size: None } => Pair(pointer_ty(tcx), pointer_ty(tcx)), - } - } + args } -pub(super) fn get_pass_mode<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> PassMode { - if layout.is_zst() { - // WARNING zst arguments must never be passed, as that will break CastKind::ClosureFnPointer - PassMode::NoPass - } else { - match &layout.abi { - Abi::Uninhabited => PassMode::NoPass, - Abi::Scalar(scalar) => PassMode::ByVal(scalar_to_clif_type(tcx, scalar.clone())), - Abi::ScalarPair(a, b) => { - let a = scalar_to_clif_type(tcx, a.clone()); - let b = scalar_to_clif_type(tcx, b.clone()); - if a == types::I128 && b == types::I128 { - // Returning (i128, i128) by-val-pair would take 4 regs, while only 3 are - // available on x86_64. Cranelift gets confused when too many return params - // are used. - PassMode::ByRef { - size: Some(layout.size), - } - } else { - PassMode::ByValPair(a, b) +impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { + fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]> { + match self.mode { + PassMode::Ignore => smallvec![], + PassMode::Direct(attrs) => match &self.layout.abi { + Abi::Scalar(scalar) => { + smallvec![apply_arg_attrs_to_abi_param( + AbiParam::new(scalar_to_clif_type(tcx, scalar.clone())), + attrs + )] } - } - - // FIXME implement Vector Abi in a cg_llvm compatible way - Abi::Vector { .. } => { - if let Some(vector_ty) = crate::intrinsics::clif_vector_type(tcx, layout) { - PassMode::ByVal(vector_ty) + Abi::Vector { .. } => { + let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap(); + smallvec![AbiParam::new(vector_ty)] + } + _ => unreachable!("{:?}", self.layout.abi), + }, + PassMode::Pair(attrs_a, attrs_b) => match &self.layout.abi { + Abi::ScalarPair(a, b) => { + let a = scalar_to_clif_type(tcx, a.clone()); + let b = scalar_to_clif_type(tcx, b.clone()); + smallvec![ + apply_arg_attrs_to_abi_param(AbiParam::new(a), attrs_a), + apply_arg_attrs_to_abi_param(AbiParam::new(b), attrs_b), + ] + } + _ => unreachable!("{:?}", self.layout.abi), + }, + PassMode::Cast(cast) => cast_target_to_abi_params(cast), + PassMode::Indirect { + attrs, + extra_attrs: None, + on_stack, + } => { + if on_stack { + let size = u32::try_from(self.layout.size.bytes()).unwrap(); + smallvec![apply_arg_attrs_to_abi_param( + AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructArgument(size),), + attrs + )] } else { - PassMode::ByRef { - size: Some(layout.size), - } + smallvec![apply_arg_attrs_to_abi_param( + AbiParam::new(pointer_ty(tcx)), + attrs + )] } } + PassMode::Indirect { + attrs, + extra_attrs: Some(extra_attrs), + on_stack, + } => { + assert!(!on_stack); + smallvec![ + apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs), + apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), extra_attrs), + ] + } + } + } - Abi::Aggregate { sized: true } => PassMode::ByRef { - size: Some(layout.size), + fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option, Vec) { + match self.mode { + PassMode::Ignore => (None, vec![]), + PassMode::Direct(_) => match &self.layout.abi { + Abi::Scalar(scalar) => ( + None, + vec![AbiParam::new(scalar_to_clif_type(tcx, scalar.clone()))], + ), + Abi::Vector { .. } => { + let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap(); + (None, vec![AbiParam::new(vector_ty)]) + } + _ => unreachable!("{:?}", self.layout.abi), + }, + PassMode::Pair(_, _) => match &self.layout.abi { + Abi::ScalarPair(a, b) => { + let a = scalar_to_clif_type(tcx, a.clone()); + let b = scalar_to_clif_type(tcx, b.clone()); + (None, vec![AbiParam::new(a), AbiParam::new(b)]) + } + _ => unreachable!("{:?}", self.layout.abi), }, - Abi::Aggregate { sized: false } => PassMode::ByRef { size: None }, + PassMode::Cast(cast) => (None, cast_target_to_abi_params(cast).into_iter().collect()), + PassMode::Indirect { + attrs: _, + extra_attrs: None, + on_stack, + } => { + assert!(!on_stack); + ( + Some(AbiParam::special( + pointer_ty(tcx), + ArgumentPurpose::StructReturn, + )), + vec![], + ) + } + PassMode::Indirect { + attrs: _, + extra_attrs: Some(_), + on_stack: _, + } => unreachable!("unsized return value"), } } } +pub(super) fn to_casted_value<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + arg: CValue<'tcx>, + cast: CastTarget, +) -> SmallVec<[Value; 2]> { + let (ptr, meta) = arg.force_stack(fx); + assert!(meta.is_none()); + let mut offset = 0; + cast_target_to_abi_params(cast) + .into_iter() + .map(|param| { + let val = ptr + .offset_i64(fx, offset) + .load(fx, param.value_type, MemFlags::new()); + offset += i64::from(param.value_type.bytes()); + val + }) + .collect() +} + +pub(super) fn from_casted_value<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + block_params: &[Value], + layout: TyAndLayout<'tcx>, + cast: CastTarget, +) -> CValue<'tcx> { + let abi_params = cast_target_to_abi_params(cast); + let abi_param_size: u32 = abi_params + .iter() + .map(|param| param.value_type.bytes()) + .sum(); + let layout_size = u32::try_from(layout.size.bytes()).unwrap(); + let stack_slot = fx.bcx.create_stack_slot(StackSlotData { + kind: StackSlotKind::ExplicitSlot, + // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to + // specify stack slot alignment. + // Stack slot size may be bigger for for example `[u8; 3]` which is packed into an `i32`. + // It may also be smaller for example when the type is a wrapper around an integer with a + // larger alignment than the integer. + size: (std::cmp::max(abi_param_size, layout_size) + 15) / 16 * 16, + offset: None, + }); + let ptr = Pointer::new(fx.bcx.ins().stack_addr(pointer_ty(fx.tcx), stack_slot, 0)); + let mut offset = 0; + let mut block_params_iter = block_params.into_iter().copied(); + for param in abi_params { + let val = ptr.offset_i64(fx, offset).store( + fx, + block_params_iter.next().unwrap(), + MemFlags::new(), + ); + offset += i64::from(param.value_type.bytes()); + val + } + assert_eq!(block_params_iter.next(), None, "Leftover block param"); + CValue::by_ref(ptr, layout) +} + /// Get a set of values to be passed as function arguments. pub(super) fn adjust_arg_for_abi<'tcx>( fx: &mut FunctionCx<'_, 'tcx, impl Module>, arg: CValue<'tcx>, -) -> EmptySinglePair { - match get_pass_mode(fx.tcx, arg.layout()) { - PassMode::NoPass => Empty, - PassMode::ByVal(_) => Single(arg.load_scalar(fx)), - PassMode::ByValPair(_, _) => { + arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, +) -> SmallVec<[Value; 2]> { + assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty); + match arg_abi.mode { + PassMode::Ignore => smallvec![], + PassMode::Direct(_) => smallvec![arg.load_scalar(fx)], + PassMode::Pair(_, _) => { let (a, b) = arg.load_scalar_pair(fx); - Pair(a, b) + smallvec![a, b] } - PassMode::ByRef { size: _ } => match arg.force_stack(fx) { - (ptr, None) => Single(ptr.get_addr(fx)), - (ptr, Some(meta)) => Pair(ptr.get_addr(fx), meta), + PassMode::Cast(cast) => to_casted_value(fx, arg, cast), + PassMode::Indirect { .. } => match arg.force_stack(fx) { + (ptr, None) => smallvec![ptr.get_addr(fx)], + (ptr, Some(meta)) => smallvec![ptr.get_addr(fx), meta], }, } } @@ -143,20 +284,23 @@ pub(super) fn adjust_arg_for_abi<'tcx>( /// as necessary. pub(super) fn cvalue_for_param<'tcx>( fx: &mut FunctionCx<'_, 'tcx, impl Module>, - start_block: Block, #[cfg_attr(not(debug_assertions), allow(unused_variables))] local: Option, #[cfg_attr(not(debug_assertions), allow(unused_variables))] local_field: Option, - arg_ty: Ty<'tcx>, + arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, + block_params_iter: &mut impl Iterator, ) -> Option> { - let layout = fx.layout_of(arg_ty); - let pass_mode = get_pass_mode(fx.tcx, layout); - - if let PassMode::NoPass = pass_mode { - return None; - } - - let clif_types = pass_mode.get_param_ty(fx.tcx); - let block_params = clif_types.map(|t| fx.bcx.append_block_param(start_block, t)); + let block_params = arg_abi + .get_abi_param(fx.tcx) + .into_iter() + .map(|abi_param| { + let block_param = block_params_iter.next().unwrap(); + assert_eq!( + fx.bcx.func.dfg.value_type(block_param), + abi_param.value_type + ); + block_param + }) + .collect::>(); #[cfg(debug_assertions)] crate::abi::comments::add_arg_comment( @@ -164,25 +308,48 @@ pub(super) fn cvalue_for_param<'tcx>( "arg", local, local_field, - block_params, - pass_mode, - arg_ty, + &block_params, + arg_abi.mode, + arg_abi.layout, ); - match pass_mode { - PassMode::NoPass => unreachable!(), - PassMode::ByVal(_) => Some(CValue::by_val(block_params.assert_single(), layout)), - PassMode::ByValPair(_, _) => { - let (a, b) = block_params.assert_pair(); - Some(CValue::by_val_pair(a, b, layout)) + match arg_abi.mode { + PassMode::Ignore => None, + PassMode::Direct(_) => { + assert_eq!(block_params.len(), 1, "{:?}", block_params); + Some(CValue::by_val(block_params[0], arg_abi.layout)) + } + PassMode::Pair(_, _) => { + assert_eq!(block_params.len(), 2, "{:?}", block_params); + Some(CValue::by_val_pair( + block_params[0], + block_params[1], + arg_abi.layout, + )) + } + PassMode::Cast(cast) => Some(from_casted_value(fx, &block_params, arg_abi.layout, cast)), + PassMode::Indirect { + attrs: _, + extra_attrs: None, + on_stack: _, + } => { + assert_eq!(block_params.len(), 1, "{:?}", block_params); + Some(CValue::by_ref( + Pointer::new(block_params[0]), + arg_abi.layout, + )) } - PassMode::ByRef { size: Some(_) } => Some(CValue::by_ref( - Pointer::new(block_params.assert_single()), - layout, - )), - PassMode::ByRef { size: None } => { - let (ptr, meta) = block_params.assert_pair(); - Some(CValue::by_ref_unsized(Pointer::new(ptr), meta, layout)) + PassMode::Indirect { + attrs: _, + extra_attrs: Some(_), + on_stack: _, + } => { + assert_eq!(block_params.len(), 2, "{:?}", block_params); + Some(CValue::by_ref_unsized( + Pointer::new(block_params[0]), + block_params[1], + arg_abi.layout, + )) } } } diff --git a/compiler/rustc_codegen_cranelift/src/abi/returning.rs b/compiler/rustc_codegen_cranelift/src/abi/returning.rs index f6d40c880d..a382963bf1 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/returning.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/returning.rs @@ -1,21 +1,57 @@ //! Return value handling -use crate::abi::pass_mode::*; use crate::prelude::*; -fn return_layout<'a, 'tcx>(fx: &mut FunctionCx<'a, 'tcx, impl Module>) -> TyAndLayout<'tcx> { - fx.layout_of(fx.monomorphize(&fx.mir.local_decls[RETURN_PLACE].ty)) -} +use rustc_middle::ty::layout::FnAbiExt; +use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; +use smallvec::{smallvec, SmallVec}; /// Can the given type be returned into an ssa var or does it need to be returned on the stack. pub(crate) fn can_return_to_ssa_var<'tcx>( - tcx: TyCtxt<'tcx>, - dest_layout: TyAndLayout<'tcx>, + fx: &FunctionCx<'_, 'tcx, impl Module>, + func: &mir::Operand<'tcx>, + args: &[mir::Operand<'tcx>], ) -> bool { - match get_pass_mode(tcx, dest_layout) { - PassMode::NoPass | PassMode::ByVal(_) | PassMode::ByValPair(_, _) => true, - // FIXME Make it possible to return ByRef to an ssa var. - PassMode::ByRef { size: _ } => false, + let fn_ty = fx.monomorphize(func.ty(fx.mir, fx.tcx)); + let fn_sig = fx + .tcx + .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), fn_ty.fn_sig(fx.tcx)); + + // Handle special calls like instrinsics and empty drop glue. + let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() { + let instance = ty::Instance::resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, substs) + .unwrap() + .unwrap() + .polymorphize(fx.tcx); + + match instance.def { + InstanceDef::Intrinsic(_) | InstanceDef::DropGlue(_, _) => { + return true; + } + _ => Some(instance), + } + } else { + None + }; + + let extra_args = &args[fn_sig.inputs().len()..]; + let extra_args = extra_args + .iter() + .map(|op_arg| fx.monomorphize(op_arg.ty(fx.mir, fx.tcx))) + .collect::>(); + let fn_abi = if let Some(instance) = instance { + FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), instance, &extra_args) + } else { + FnAbi::of_fn_ptr( + &RevealAllLayoutCx(fx.tcx), + fn_ty.fn_sig(fx.tcx), + &extra_args, + ) + }; + match fn_abi.ret.mode { + PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) => true, + // FIXME Make it possible to return Cast and Indirect to an ssa var. + PassMode::Cast(_) | PassMode::Indirect { .. } => false, } } @@ -24,27 +60,45 @@ pub(crate) fn can_return_to_ssa_var<'tcx>( pub(super) fn codegen_return_param<'tcx>( fx: &mut FunctionCx<'_, 'tcx, impl Module>, ssa_analyzed: &rustc_index::vec::IndexVec, - start_block: Block, + block_params_iter: &mut impl Iterator, ) -> CPlace<'tcx> { - let ret_layout = return_layout(fx); - let ret_pass_mode = get_pass_mode(fx.tcx, ret_layout); - let (ret_place, ret_param) = match ret_pass_mode { - PassMode::NoPass => (CPlace::no_place(ret_layout), Empty), - PassMode::ByVal(_) | PassMode::ByValPair(_, _) => { + let (ret_place, ret_param): (_, SmallVec<[_; 2]>) = match fx.fn_abi.as_ref().unwrap().ret.mode { + PassMode::Ignore => ( + CPlace::no_place(fx.fn_abi.as_ref().unwrap().ret.layout), + smallvec![], + ), + PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(_) => { let is_ssa = ssa_analyzed[RETURN_PLACE] == crate::analyze::SsaKind::Ssa; ( - super::make_local_place(fx, RETURN_PLACE, ret_layout, is_ssa), - Empty, + super::make_local_place( + fx, + RETURN_PLACE, + fx.fn_abi.as_ref().unwrap().ret.layout, + is_ssa, + ), + smallvec![], ) } - PassMode::ByRef { size: Some(_) } => { - let ret_param = fx.bcx.append_block_param(start_block, fx.pointer_type); + PassMode::Indirect { + attrs: _, + extra_attrs: None, + on_stack: _, + } => { + let ret_param = block_params_iter.next().unwrap(); + assert_eq!(fx.bcx.func.dfg.value_type(ret_param), pointer_ty(fx.tcx)); ( - CPlace::for_ptr(Pointer::new(ret_param), ret_layout), - Single(ret_param), + CPlace::for_ptr( + Pointer::new(ret_param), + fx.fn_abi.as_ref().unwrap().ret.layout, + ), + smallvec![ret_param], ) } - PassMode::ByRef { size: None } => todo!(), + PassMode::Indirect { + attrs: _, + extra_attrs: Some(_), + on_stack: _, + } => unreachable!("unsized return value"), }; #[cfg(not(debug_assertions))] @@ -56,9 +110,9 @@ pub(super) fn codegen_return_param<'tcx>( "ret", Some(RETURN_PLACE), None, - ret_param, - ret_pass_mode, - ret_layout.ty, + &ret_param, + fx.fn_abi.as_ref().unwrap().ret.mode, + fx.fn_abi.as_ref().unwrap().ret.layout, ); ret_place @@ -68,42 +122,71 @@ pub(super) fn codegen_return_param<'tcx>( /// returns the call return value(s) if any are written to the correct place. pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>( fx: &mut FunctionCx<'_, 'tcx, M>, - fn_sig: FnSig<'tcx>, + ret_arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, ret_place: Option>, f: impl FnOnce(&mut FunctionCx<'_, 'tcx, M>, Option) -> (Inst, T), ) -> (Inst, T) { - let ret_layout = fx.layout_of(fn_sig.output()); - - let output_pass_mode = get_pass_mode(fx.tcx, ret_layout); - let return_ptr = match output_pass_mode { - PassMode::NoPass => None, - PassMode::ByRef { size: Some(_) } => match ret_place { + let return_ptr = match ret_arg_abi.mode { + PassMode::Ignore => None, + PassMode::Indirect { + attrs: _, + extra_attrs: None, + on_stack: _, + } => match ret_place { Some(ret_place) => Some(ret_place.to_ptr().get_addr(fx)), None => Some(fx.bcx.ins().iconst(fx.pointer_type, 43)), // FIXME allocate temp stack slot }, - PassMode::ByRef { size: None } => todo!(), - PassMode::ByVal(_) | PassMode::ByValPair(_, _) => None, + PassMode::Indirect { + attrs: _, + extra_attrs: Some(_), + on_stack: _, + } => unreachable!("unsized return value"), + PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(_) => None, }; let (call_inst, meta) = f(fx, return_ptr); - match output_pass_mode { - PassMode::NoPass => {} - PassMode::ByVal(_) => { + match ret_arg_abi.mode { + PassMode::Ignore => {} + PassMode::Direct(_) => { if let Some(ret_place) = ret_place { let ret_val = fx.bcx.inst_results(call_inst)[0]; - ret_place.write_cvalue(fx, CValue::by_val(ret_val, ret_layout)); + ret_place.write_cvalue(fx, CValue::by_val(ret_val, ret_arg_abi.layout)); } } - PassMode::ByValPair(_, _) => { + PassMode::Pair(_, _) => { if let Some(ret_place) = ret_place { let ret_val_a = fx.bcx.inst_results(call_inst)[0]; let ret_val_b = fx.bcx.inst_results(call_inst)[1]; - ret_place.write_cvalue(fx, CValue::by_val_pair(ret_val_a, ret_val_b, ret_layout)); + ret_place.write_cvalue( + fx, + CValue::by_val_pair(ret_val_a, ret_val_b, ret_arg_abi.layout), + ); } } - PassMode::ByRef { size: Some(_) } => {} - PassMode::ByRef { size: None } => todo!(), + PassMode::Cast(cast) => { + if let Some(ret_place) = ret_place { + let results = fx + .bcx + .inst_results(call_inst) + .into_iter() + .copied() + .collect::>(); + let result = + super::pass_mode::from_casted_value(fx, &results, ret_place.layout(), cast); + ret_place.write_cvalue(fx, result); + } + } + PassMode::Indirect { + attrs: _, + extra_attrs: None, + on_stack: _, + } => {} + PassMode::Indirect { + attrs: _, + extra_attrs: Some(_), + on_stack: _, + } => unreachable!("unsized return value"), } (call_inst, meta) @@ -111,20 +194,35 @@ pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>( /// Codegen a return instruction with the right return value(s) if any. pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, impl Module>) { - match get_pass_mode(fx.tcx, return_layout(fx)) { - PassMode::NoPass | PassMode::ByRef { size: Some(_) } => { + match fx.fn_abi.as_ref().unwrap().ret.mode { + PassMode::Ignore + | PassMode::Indirect { + attrs: _, + extra_attrs: None, + on_stack: _, + } => { fx.bcx.ins().return_(&[]); } - PassMode::ByRef { size: None } => todo!(), - PassMode::ByVal(_) => { + PassMode::Indirect { + attrs: _, + extra_attrs: Some(_), + on_stack: _, + } => unreachable!("unsized return value"), + PassMode::Direct(_) => { let place = fx.get_local_place(RETURN_PLACE); let ret_val = place.to_cvalue(fx).load_scalar(fx); fx.bcx.ins().return_(&[ret_val]); } - PassMode::ByValPair(_, _) => { + PassMode::Pair(_, _) => { let place = fx.get_local_place(RETURN_PLACE); let (ret_val_a, ret_val_b) = place.to_cvalue(fx).load_scalar_pair(fx); fx.bcx.ins().return_(&[ret_val_a, ret_val_b]); } + PassMode::Cast(cast) => { + let place = fx.get_local_place(RETURN_PLACE); + let ret_val = place.to_cvalue(fx); + let ret_vals = super::pass_mode::to_casted_value(fx, ret_val, cast); + fx.bcx.ins().return_(&ret_vals); + } } } diff --git a/compiler/rustc_codegen_cranelift/src/analyze.rs b/compiler/rustc_codegen_cranelift/src/analyze.rs index adf5c7ac4f..62fbcfe3f7 100644 --- a/compiler/rustc_codegen_cranelift/src/analyze.rs +++ b/compiler/rustc_codegen_cranelift/src/analyze.rs @@ -40,11 +40,14 @@ pub(crate) fn analyze(fx: &FunctionCx<'_, '_, impl Module>) -> IndexVec { + TerminatorKind::Call { + destination, + func, + args, + .. + } => { if let Some((dest_place, _dest_bb)) = destination { - let dest_layout = fx - .layout_of(fx.monomorphize(&dest_place.ty(&fx.mir.local_decls, fx.tcx).ty)); - if !crate::abi::can_return_to_ssa_var(fx.tcx, dest_layout) { + if !crate::abi::can_return_to_ssa_var(fx, func, args) { not_ssa(&mut flag_map, dest_place.local) } } diff --git a/compiler/rustc_codegen_cranelift/src/backend.rs b/compiler/rustc_codegen_cranelift/src/backend.rs index 9e32259716..0ce34c904b 100644 --- a/compiler/rustc_codegen_cranelift/src/backend.rs +++ b/compiler/rustc_codegen_cranelift/src/backend.rs @@ -162,7 +162,7 @@ impl AddConstructor for ObjectProduct { } pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object)) -> Vec { - let triple = crate::build_isa(sess, true).triple().clone(); + let triple = crate::build_isa(sess).triple().clone(); let binary_format = match triple.binary_format { target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf, @@ -193,7 +193,7 @@ pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object pub(crate) fn make_module(sess: &Session, name: String) -> ObjectModule { let mut builder = ObjectBuilder::new( - crate::build_isa(sess, true), + crate::build_isa(sess), name + ".o", cranelift_module::default_libcall_names(), ) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 72073896a7..4842628a99 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -2,6 +2,8 @@ use rustc_index::vec::IndexVec; use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::layout::FnAbiExt; +use rustc_target::abi::call::FnAbi; use crate::prelude::*; @@ -19,7 +21,8 @@ pub(crate) fn codegen_fn<'tcx>( let mir = tcx.instance_mir(instance.def); // Declare function - let (name, sig) = get_function_name_and_sig(tcx, cx.module.isa().triple(), instance, false); + let name = tcx.symbol_name(instance).name.to_string(); + let sig = get_function_sig(tcx, cx.module.isa().triple(), instance); let func_id = cx.module.declare_function(&name, linkage, &sig).unwrap(); cx.cached_context.clear(); @@ -50,6 +53,7 @@ pub(crate) fn codegen_fn<'tcx>( instance, mir, + fn_abi: Some(FnAbi::of_instance(&RevealAllLayoutCx(tcx), instance, &[])), bcx, block_map, @@ -117,6 +121,11 @@ pub(crate) fn codegen_fn<'tcx>( context.compute_domtree(); context.eliminate_unreachable_code(cx.module.isa()).unwrap(); context.dce(cx.module.isa()).unwrap(); + // Some Cranelift optimizations expect the domtree to not yet be computed and as such don't + // invalidate it when it would change. + context.domtree.clear(); + + context.want_disasm = crate::pretty_clif::should_write_ir(tcx); // Define function let module = &mut cx.module; @@ -140,6 +149,16 @@ pub(crate) fn codegen_fn<'tcx>( &clif_comments, ); + if let Some(mach_compile_result) = &context.mach_compile_result { + if let Some(disasm) = &mach_compile_result.disasm { + crate::pretty_clif::write_ir_file( + tcx, + &format!("{}.vcode", tcx.symbol_name(instance).name), + |file| file.write_all(disasm.as_bytes()), + ) + } + } + // Define debuginfo for function let isa = cx.module.isa(); let debug_context = &mut cx.debug_context; @@ -307,7 +326,9 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { } => { let discr = codegen_operand(fx, discr).load_scalar(fx); - if switch_ty.kind() == fx.tcx.types.bool.kind() { + let use_bool_opt = switch_ty.kind() == fx.tcx.types.bool.kind() + || (targets.iter().count() == 1 && targets.iter().next().unwrap().0 == 0); + if use_bool_opt { assert_eq!(targets.iter().count(), 1); let (then_value, then_block) = targets.iter().next().unwrap(); let then_block = fx.get_block(then_block); @@ -325,12 +346,22 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { let discr = crate::optimize::peephole::maybe_unwrap_bint(&mut fx.bcx, discr); let discr = crate::optimize::peephole::make_branchable_value(&mut fx.bcx, discr); - if test_zero { - fx.bcx.ins().brz(discr, then_block, &[]); - fx.bcx.ins().jump(else_block, &[]); + if let Some(taken) = crate::optimize::peephole::maybe_known_branch_taken( + &fx.bcx, discr, test_zero, + ) { + if taken { + fx.bcx.ins().jump(then_block, &[]); + } else { + fx.bcx.ins().jump(else_block, &[]); + } } else { - fx.bcx.ins().brnz(discr, then_block, &[]); - fx.bcx.ins().jump(else_block, &[]); + if test_zero { + fx.bcx.ins().brz(discr, then_block, &[]); + fx.bcx.ins().jump(else_block, &[]); + } else { + fx.bcx.ins().brnz(discr, then_block, &[]); + fx.bcx.ins().jump(else_block, &[]); + } } } else { let mut switch = ::cranelift_frontend::Switch::new(); @@ -1029,7 +1060,11 @@ pub(crate) fn codegen_panic_inner<'tcx>( fx.lib_call( &*symbol_name, - vec![fx.pointer_type, fx.pointer_type, fx.pointer_type], + vec![ + AbiParam::new(fx.pointer_type), + AbiParam::new(fx.pointer_type), + AbiParam::new(fx.pointer_type), + ], vec![], args, ); diff --git a/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs b/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs index f4d23ebcf4..be369b07fd 100644 --- a/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs +++ b/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs @@ -6,7 +6,7 @@ extern crate rustc_interface; extern crate rustc_session; extern crate rustc_target; -use rustc_data_structures::profiling::print_time_passes_entry; +use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_interface::interface; use rustc_session::config::ErrorOutputType; use rustc_session::early_error; @@ -39,14 +39,13 @@ impl rustc_driver::Callbacks for CraneliftPassesCallbacks { } fn main() { - let start = std::time::Instant::now(); + let start_time = std::time::Instant::now(); + let start_rss = get_resident_set_size(); rustc_driver::init_rustc_env_logger(); let mut callbacks = CraneliftPassesCallbacks::default(); rustc_driver::install_ice_hook(); let exit_code = rustc_driver::catch_with_exit_code(|| { - let mut use_jit = false; - - let mut args = std::env::args_os() + let args = std::env::args_os() .enumerate() .map(|(i, arg)| { arg.into_string().unwrap_or_else(|arg| { @@ -56,27 +55,18 @@ fn main() { ) }) }) - .filter(|arg| { - if arg == "--jit" { - use_jit = true; - false - } else { - true - } - }) .collect::>(); - if use_jit { - args.push("-Cprefer-dynamic".to_string()); - } let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks); run_compiler.set_make_codegen_backend(Some(Box::new(move |_| { - Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { - config: rustc_codegen_cranelift::BackendConfig { use_jit }, - }) + Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { config: None }) }))); run_compiler.run() }); - // The extra `\t` is necessary to align this label with the others. - print_time_passes_entry(callbacks.time_passes, "\ttotal", start.elapsed()); + + if callbacks.time_passes { + let end_rss = get_resident_set_size(); + print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss); + } + std::process::exit(exit_code) } diff --git a/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs b/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs index 165d33dcfb..83e5dc6e67 100644 --- a/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs @@ -53,10 +53,7 @@ impl rustc_driver::Callbacks for CraneliftPassesCallbacks { .unwrap() .parent() .unwrap() - .parent() - .unwrap() - .join("build_sysroot") - .join("sysroot"), + .to_owned(), ); } } @@ -92,9 +89,7 @@ fn main() { let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks); if use_clif { run_compiler.set_make_codegen_backend(Some(Box::new(move |_| { - Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { - config: rustc_codegen_cranelift::BackendConfig { use_jit: false }, - }) + Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { config: None }) }))); } run_compiler.run() diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index d6a38bdafc..866ba90e4a 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -1,5 +1,7 @@ //! Replaces 128-bit operators with lang item calls where necessary +use cranelift_codegen::ir::ArgumentPurpose; + use crate::prelude::*; pub(crate) fn maybe_codegen<'tcx>( @@ -24,41 +26,41 @@ pub(crate) fn maybe_codegen<'tcx>( None } BinOp::Add | BinOp::Sub if !checked => None, - BinOp::Add => { - let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); - return Some(if is_signed { - fx.easy_call("__rust_i128_addo", &[lhs, rhs], out_ty) + BinOp::Mul if !checked => { + let val_ty = if is_signed { + fx.tcx.types.i128 } else { - fx.easy_call("__rust_u128_addo", &[lhs, rhs], out_ty) - }); + fx.tcx.types.u128 + }; + Some(fx.easy_call("__multi3", &[lhs, rhs], val_ty)) } - BinOp::Sub => { + BinOp::Add | BinOp::Sub | BinOp::Mul => { + assert!(checked); let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); - return Some(if is_signed { - fx.easy_call("__rust_i128_subo", &[lhs, rhs], out_ty) - } else { - fx.easy_call("__rust_u128_subo", &[lhs, rhs], out_ty) - }); - } - BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), - BinOp::Mul => { - let res = if checked { - let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); - if is_signed { - fx.easy_call("__rust_i128_mulo", &[lhs, rhs], out_ty) - } else { - fx.easy_call("__rust_u128_mulo", &[lhs, rhs], out_ty) - } - } else { - let val_ty = if is_signed { - fx.tcx.types.i128 - } else { - fx.tcx.types.u128 - }; - fx.easy_call("__multi3", &[lhs, rhs], val_ty) + let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty)); + let param_types = vec![ + AbiParam::special(pointer_ty(fx.tcx), ArgumentPurpose::StructReturn), + AbiParam::new(types::I128), + AbiParam::new(types::I128), + ]; + let args = [ + out_place.to_ptr().get_addr(fx), + lhs.load_scalar(fx), + rhs.load_scalar(fx), + ]; + let name = match (bin_op, is_signed) { + (BinOp::Add, false) => "__rust_u128_addo", + (BinOp::Add, true) => "__rust_i128_addo", + (BinOp::Sub, false) => "__rust_u128_subo", + (BinOp::Sub, true) => "__rust_i128_subo", + (BinOp::Mul, false) => "__rust_u128_mulo", + (BinOp::Mul, true) => "__rust_i128_mulo", + _ => unreachable!(), }; - Some(res) + fx.lib_call(name, param_types, vec![], &args); + Some(out_place.to_cvalue(fx)) } + BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), BinOp::Div => { assert!(!checked); if is_signed { diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 1485d4451b..fbee84e09f 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -1,4 +1,5 @@ use rustc_index::vec::IndexVec; +use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Integer, Primitive}; use rustc_target::spec::{HasTargetSpec, Target}; @@ -294,6 +295,7 @@ pub(crate) struct FunctionCx<'clif, 'tcx, M: Module> { pub(crate) instance: Instance<'tcx>, pub(crate) mir: &'tcx Body<'tcx>, + pub(crate) fn_abi: Option>>, pub(crate) bcx: FunctionBuilder<'clif>, pub(crate) block_map: IndexVec, @@ -319,16 +321,7 @@ impl<'tcx, M: Module> LayoutOf for FunctionCx<'_, 'tcx, M> { type TyAndLayout = TyAndLayout<'tcx>; fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> { - assert!(!ty.still_further_specializable()); - self.tcx - .layout_of(ParamEnv::reveal_all().and(&ty)) - .unwrap_or_else(|e| { - if let layout::LayoutError::SizeOverflow(_) = e { - self.tcx.sess.fatal(&e.to_string()) - } else { - bug!("failed to get layout for `{}`: {}", ty, e) - } - }) + RevealAllLayoutCx(self.tcx).layout_of(ty) } } @@ -442,3 +435,47 @@ impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { self.bcx.ins().global_value(self.pointer_type, local_msg_id) } } + +pub(crate) struct RevealAllLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>); + +impl<'tcx> LayoutOf for RevealAllLayoutCx<'tcx> { + type Ty = Ty<'tcx>; + type TyAndLayout = TyAndLayout<'tcx>; + + fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> { + assert!(!ty.still_further_specializable()); + self.0 + .layout_of(ParamEnv::reveal_all().and(&ty)) + .unwrap_or_else(|e| { + if let layout::LayoutError::SizeOverflow(_) = e { + self.0.sess.fatal(&e.to_string()) + } else { + bug!("failed to get layout for `{}`: {}", ty, e) + } + }) + } +} + +impl<'tcx> layout::HasTyCtxt<'tcx> for RevealAllLayoutCx<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.0 + } +} + +impl<'tcx> rustc_target::abi::HasDataLayout for RevealAllLayoutCx<'tcx> { + fn data_layout(&self) -> &rustc_target::abi::TargetDataLayout { + &self.0.data_layout + } +} + +impl<'tcx> layout::HasParamEnv<'tcx> for RevealAllLayoutCx<'tcx> { + fn param_env(&self) -> ParamEnv<'tcx> { + ParamEnv::reveal_all() + } +} + +impl<'tcx> HasTargetSpec for RevealAllLayoutCx<'tcx> { + fn target_spec(&self) -> &Target { + &self.0.sess.target + } +} diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 544b020b71..5702832bcb 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -100,7 +100,10 @@ fn codegen_static_ref<'tcx>( let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id); assert!(!layout.is_unsized(), "unsized statics aren't supported"); assert!( - matches!(fx.bcx.func.global_values[local_data_id], GlobalValueData::Symbol { tls: false, ..}), + matches!( + fx.bcx.func.global_values[local_data_id], + GlobalValueData::Symbol { tls: false, .. } + ), "tls static referenced without Rvalue::ThreadLocalRef" ); CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout) @@ -131,11 +134,9 @@ pub(crate) fn codegen_constant<'tcx>( { Ok(const_val) => const_val, Err(_) => { - if promoted.is_none() { - fx.tcx - .sess - .span_err(constant.span, "erroneous constant encountered"); - } + fx.tcx + .sess + .span_err(constant.span, "erroneous constant encountered"); return crate::trap::trap_unreachable_ret_value( fx, fx.layout_of(const_.ty), @@ -447,7 +448,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut Constan data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64); } - module.define_data(data_id, &data_ctx).unwrap(); + // FIXME don't duplicate definitions in lazy jit mode + let _ = module.define_data(data_id, &data_ctx); cx.done.insert(data_id); } diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs index c21835b1fc..6160f9b78d 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs @@ -74,10 +74,7 @@ impl WriterRelocate { /// Perform the collected relocations to be usable for JIT usage. #[cfg(feature = "jit")] - pub(super) fn relocate_for_jit( - mut self, - jit_module: &cranelift_simplejit::SimpleJITModule, - ) -> Vec { + pub(super) fn relocate_for_jit(mut self, jit_module: &cranelift_jit::JITModule) -> Vec { use std::convert::TryInto; for reloc in self.relocs.drain(..) { diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs index e0f62b64e6..49de927cdb 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -15,11 +15,11 @@ pub(crate) struct UnwindContext<'tcx> { } impl<'tcx> UnwindContext<'tcx> { - pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self { + pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa, pic_eh_frame: bool) -> Self { let mut frame_table = FrameTable::default(); let cie_id = if let Some(mut cie) = isa.create_systemv_cie() { - if isa.flags().is_pic() { + if pic_eh_frame { cie.fde_address_encoding = gimli::DwEhPe(gimli::DW_EH_PE_pcrel.0 | gimli::DW_EH_PE_sdata4.0); } @@ -80,7 +80,7 @@ impl<'tcx> UnwindContext<'tcx> { #[cfg(feature = "jit")] pub(crate) unsafe fn register_jit( self, - jit_module: &cranelift_simplejit::SimpleJITModule, + jit_module: &cranelift_jit::JITModule, ) -> Option { let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian( self.tcx, diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 78d6ff0cb0..df89883f0b 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -8,7 +8,7 @@ use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::cstore::EncodedMetadata; -use rustc_middle::mir::mono::CodegenUnit; +use rustc_middle::mir::mono::{CodegenUnit, MonoItem}; use rustc_session::cgu_reuse_tracker::CguReuse; use rustc_session::config::{DebugInfo, OutputType}; @@ -146,11 +146,34 @@ fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodege } } - let mut cx = crate::CodegenCx::new(tcx, module, tcx.sess.opts.debuginfo != DebugInfo::None); + let mut cx = crate::CodegenCx::new( + tcx, + module, + tcx.sess.opts.debuginfo != DebugInfo::None, + true, + ); super::predefine_mono_items(&mut cx, &mono_items); for (mono_item, (linkage, visibility)) in mono_items { let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); - super::codegen_mono_item(&mut cx, mono_item, linkage); + match mono_item { + MonoItem::Fn(inst) => { + cx.tcx.sess.time("codegen fn", || { + crate::base::codegen_fn(&mut cx, inst, linkage) + }); + } + MonoItem::Static(def_id) => { + crate::constant::codegen_static(&mut cx.constants_cx, def_id) + } + MonoItem::GlobalAsm(hir_id) => { + let item = cx.tcx.hir().expect_item(hir_id); + if let rustc_hir::ItemKind::GlobalAsm(rustc_hir::GlobalAsm { asm }) = item.kind { + cx.global_asm.push_str(&*asm.as_str()); + cx.global_asm.push_str("\n\n"); + } else { + bug!("Expected GlobalAsm found {:?}", item); + } + } + } } let (mut module, global_asm, debug, mut unwind_context) = tcx.sess.time("finalize CodegenCx", || cx.finalize()); @@ -236,7 +259,7 @@ pub(super) fn run_aot( tcx.sess.abort_if_errors(); let mut allocator_module = new_module(tcx, "allocator_shim".to_string()); - let mut allocator_unwind_context = UnwindContext::new(tcx, allocator_module.isa()); + let mut allocator_unwind_context = UnwindContext::new(tcx, allocator_module.isa(), true); let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module, &mut allocator_unwind_context); @@ -258,9 +281,6 @@ pub(super) fn run_aot( None }; - rustc_incremental::assert_dep_graph(tcx); - rustc_incremental::save_dep_graph(tcx); - let metadata_module = if need_metadata_module { let _timer = tcx.prof.generic_activity("codegen crate metadata"); let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || { @@ -299,10 +319,6 @@ pub(super) fn run_aot( None }; - if tcx.sess.opts.output_types.should_codegen() { - rustc_incremental::assert_module_sources::assert_module_sources(tcx); - } - Box::new(( CodegenResults { crate_name: tcx.crate_name(LOCAL_CRATE), diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 5a844841c2..2d14ff2c02 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -1,16 +1,23 @@ //! The JIT driver uses [`cranelift_simplejit`] to JIT execute programs without writing any object //! files. +use std::cell::RefCell; use std::ffi::CString; use std::os::raw::{c_char, c_int}; use rustc_codegen_ssa::CrateInfo; +use rustc_middle::mir::mono::MonoItem; -use cranelift_simplejit::{SimpleJITBuilder, SimpleJITModule}; +use cranelift_jit::{JITBuilder, JITModule}; use crate::prelude::*; +use crate::{CodegenCx, CodegenMode}; -pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { +thread_local! { + pub static CURRENT_MODULE: RefCell> = RefCell::new(None); +} + +pub(super) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode) -> ! { if !tcx.sess.opts.output_types.should_codegen() { tcx.sess.fatal("JIT mode doesn't work with `cargo check`."); } @@ -35,12 +42,13 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { let imported_symbols = load_imported_symbols_for_jit(tcx); - let mut jit_builder = SimpleJITBuilder::with_isa( - crate::build_isa(tcx.sess, false), + let mut jit_builder = JITBuilder::with_isa( + crate::build_isa(tcx.sess), cranelift_module::default_libcall_names(), ); + jit_builder.hotswap(matches!(codegen_mode, CodegenMode::JitLazy)); jit_builder.symbols(imported_symbols); - let mut jit_module = SimpleJITModule::new(jit_builder); + let mut jit_module = JITModule::new(jit_builder); assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type()); let sig = Signature { @@ -66,20 +74,42 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { .into_iter() .collect::>(); - let mut cx = crate::CodegenCx::new(tcx, jit_module, false); + let mut cx = crate::CodegenCx::new(tcx, jit_module, false, false); - let (mut jit_module, global_asm, _debug, mut unwind_context) = - super::time(tcx, "codegen mono items", || { - super::predefine_mono_items(&mut cx, &mono_items); - for (mono_item, (linkage, visibility)) in mono_items { - let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); - super::codegen_mono_item(&mut cx, mono_item, linkage); + super::time(tcx, "codegen mono items", || { + super::predefine_mono_items(&mut cx, &mono_items); + for (mono_item, (linkage, visibility)) in mono_items { + let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); + match mono_item { + MonoItem::Fn(inst) => match codegen_mode { + CodegenMode::Aot => unreachable!(), + CodegenMode::Jit => { + cx.tcx.sess.time("codegen fn", || { + crate::base::codegen_fn(&mut cx, inst, linkage) + }); + } + CodegenMode::JitLazy => codegen_shim(&mut cx, inst), + }, + MonoItem::Static(def_id) => { + crate::constant::codegen_static(&mut cx.constants_cx, def_id); + } + MonoItem::GlobalAsm(hir_id) => { + let item = cx.tcx.hir().expect_item(hir_id); + tcx.sess + .span_fatal(item.span, "Global asm is not supported in JIT mode"); + } } - tcx.sess.time("finalize CodegenCx", || cx.finalize()) - }); + } + }); + + let (mut jit_module, global_asm, _debug, mut unwind_context) = + tcx.sess.time("finalize CodegenCx", || cx.finalize()); + jit_module.finalize_definitions(); + if !global_asm.is_empty() { - tcx.sess.fatal("Global asm is not supported in JIT mode"); + tcx.sess.fatal("Inline asm is not supported in JIT mode"); } + crate::main_shim::maybe_create_entry_wrapper(tcx, &mut jit_module, &mut unwind_context, true); crate::allocator::codegen(tcx, &mut jit_module, &mut unwind_context); @@ -91,7 +121,7 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { let finalized_main: *const u8 = jit_module.get_finalized_function(main_func_id); - println!("Rustc codegen cranelift will JIT run the executable, because --jit was passed"); + println!("Rustc codegen cranelift will JIT run the executable, because -Cllvm-args=mode=jit was passed"); let f: extern "C" fn(c_int, *const *const c_char) -> c_int = unsafe { ::std::mem::transmute(finalized_main) }; @@ -107,11 +137,46 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { // useful as some dynamic linkers use it as a marker to jump over. argv.push(std::ptr::null()); + CURRENT_MODULE + .with(|current_module| assert!(current_module.borrow_mut().replace(jit_module).is_none())); + let ret = f(args.len() as c_int, argv.as_ptr()); std::process::exit(ret); } +#[no_mangle] +extern "C" fn __clif_jit_fn(instance_ptr: *const Instance<'static>) -> *const u8 { + rustc_middle::ty::tls::with(|tcx| { + // lift is used to ensure the correct lifetime for instance. + let instance = tcx.lift(unsafe { *instance_ptr }).unwrap(); + + CURRENT_MODULE.with(|jit_module| { + let mut jit_module = jit_module.borrow_mut(); + let jit_module = jit_module.as_mut().unwrap(); + let mut cx = crate::CodegenCx::new(tcx, jit_module, false, false); + + let name = tcx.symbol_name(instance).name.to_string(); + let sig = crate::abi::get_function_sig(tcx, cx.module.isa().triple(), instance); + let func_id = cx + .module + .declare_function(&name, Linkage::Export, &sig) + .unwrap(); + cx.module.prepare_for_function_redefine(func_id).unwrap(); + + tcx.sess.time("codegen fn", || { + crate::base::codegen_fn(&mut cx, instance, Linkage::Export) + }); + + let (jit_module, global_asm, _debug_context, unwind_context) = cx.finalize(); + assert!(global_asm.is_empty()); + jit_module.finalize_definitions(); + std::mem::forget(unsafe { unwind_context.register_jit(&jit_module) }); + jit_module.get_finalized_function(func_id) + }) + }) +} + fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> { use rustc_middle::middle::dependency_format::Linkage; @@ -171,3 +236,68 @@ fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> { imported_symbols } + +pub(super) fn codegen_shim<'tcx>(cx: &mut CodegenCx<'tcx, impl Module>, inst: Instance<'tcx>) { + let tcx = cx.tcx; + + let pointer_type = cx.module.target_config().pointer_type(); + + let name = tcx.symbol_name(inst).name.to_string(); + let sig = crate::abi::get_function_sig(tcx, cx.module.isa().triple(), inst); + let func_id = cx + .module + .declare_function(&name, Linkage::Export, &sig) + .unwrap(); + + let instance_ptr = Box::into_raw(Box::new(inst)); + + let jit_fn = cx + .module + .declare_function( + "__clif_jit_fn", + Linkage::Import, + &Signature { + call_conv: cx.module.target_config().default_call_conv, + params: vec![AbiParam::new(pointer_type)], + returns: vec![AbiParam::new(pointer_type)], + }, + ) + .unwrap(); + + let mut trampoline = Function::with_name_signature(ExternalName::default(), sig.clone()); + let mut builder_ctx = FunctionBuilderContext::new(); + let mut trampoline_builder = FunctionBuilder::new(&mut trampoline, &mut builder_ctx); + + let jit_fn = cx + .module + .declare_func_in_func(jit_fn, trampoline_builder.func); + let sig_ref = trampoline_builder.func.import_signature(sig); + + let entry_block = trampoline_builder.create_block(); + trampoline_builder.append_block_params_for_function_params(entry_block); + let fn_args = trampoline_builder + .func + .dfg + .block_params(entry_block) + .to_vec(); + + trampoline_builder.switch_to_block(entry_block); + let instance_ptr = trampoline_builder + .ins() + .iconst(pointer_type, instance_ptr as u64 as i64); + let jitted_fn = trampoline_builder.ins().call(jit_fn, &[instance_ptr]); + let jitted_fn = trampoline_builder.func.dfg.inst_results(jitted_fn)[0]; + let call_inst = trampoline_builder + .ins() + .call_indirect(sig_ref, jitted_fn, &fn_args); + let ret_vals = trampoline_builder.func.dfg.inst_results(call_inst).to_vec(); + trampoline_builder.ins().return_(&ret_vals); + + cx.module + .define_function( + func_id, + &mut Context::for_function(trampoline), + &mut cranelift_codegen::binemit::NullTrapSink {}, + ) + .unwrap(); +} diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index 7b8cc2ddd4..2497f9dfdf 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -7,6 +7,7 @@ use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::mir::mono::{Linkage as RLinkage, MonoItem, Visibility}; use crate::prelude::*; +use crate::CodegenMode; mod aot; #[cfg(feature = "jit")] @@ -20,24 +21,25 @@ pub(crate) fn codegen_crate( ) -> Box { tcx.sess.abort_if_errors(); - if config.use_jit { - let is_executable = tcx - .sess - .crate_types() - .contains(&rustc_session::config::CrateType::Executable); - if !is_executable { - tcx.sess.fatal("can't jit non-executable crate"); - } + match config.codegen_mode { + CodegenMode::Aot => aot::run_aot(tcx, metadata, need_metadata_module), + CodegenMode::Jit | CodegenMode::JitLazy => { + let is_executable = tcx + .sess + .crate_types() + .contains(&rustc_session::config::CrateType::Executable); + if !is_executable { + tcx.sess.fatal("can't jit non-executable crate"); + } - #[cfg(feature = "jit")] - let _: ! = jit::run_jit(tcx); + #[cfg(feature = "jit")] + let _: ! = jit::run_jit(tcx, config.codegen_mode); - #[cfg(not(feature = "jit"))] - tcx.sess - .fatal("jit support was disabled when compiling rustc_codegen_cranelift"); + #[cfg(not(feature = "jit"))] + tcx.sess + .fatal("jit support was disabled when compiling rustc_codegen_cranelift"); + } } - - aot::run_aot(tcx, metadata, need_metadata_module) } fn predefine_mono_items<'tcx>( @@ -48,12 +50,9 @@ fn predefine_mono_items<'tcx>( for &(mono_item, (linkage, visibility)) in mono_items { match mono_item { MonoItem::Fn(instance) => { - let (name, sig) = get_function_name_and_sig( - cx.tcx, - cx.module.isa().triple(), - instance, - false, - ); + let name = cx.tcx.symbol_name(instance).name.to_string(); + let _inst_guard = crate::PrintOnPanic(|| format!("{:?} {}", instance, name)); + let sig = get_function_sig(cx.tcx, cx.module.isa().triple(), instance); let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); cx.module.declare_function(&name, linkage, &sig).unwrap(); } @@ -63,30 +62,6 @@ fn predefine_mono_items<'tcx>( }); } -fn codegen_mono_item<'tcx, M: Module>( - cx: &mut crate::CodegenCx<'tcx, M>, - mono_item: MonoItem<'tcx>, - linkage: Linkage, -) { - match mono_item { - MonoItem::Fn(inst) => { - cx.tcx - .sess - .time("codegen fn", || crate::base::codegen_fn(cx, inst, linkage)); - } - MonoItem::Static(def_id) => crate::constant::codegen_static(&mut cx.constants_cx, def_id), - MonoItem::GlobalAsm(hir_id) => { - let item = cx.tcx.hir().expect_item(hir_id); - if let rustc_hir::ItemKind::GlobalAsm(rustc_hir::GlobalAsm { asm }) = item.kind { - cx.global_asm.push_str(&*asm.as_str()); - cx.global_asm.push_str("\n\n"); - } else { - bug!("Expected GlobalAsm found {:?}", item); - } - } - } -} - fn time(tcx: TyCtxt<'_>, name: &'static str, f: impl FnOnce() -> R) -> R { if std::env::var("CG_CLIF_DISPLAY_CG_TIME") .as_ref() diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index 171445f2d7..d58e4d4995 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -23,8 +23,8 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( // Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8` llvm.x86.sse2.pmovmskb.128 | llvm.x86.avx2.pmovmskb | llvm.x86.sse2.movmsk.pd, (c a) { - let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, a.layout()); - let lane_ty = fx.clif_type(lane_layout.ty).unwrap(); + let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx); + let lane_ty = fx.clif_type(lane_ty).unwrap(); assert!(lane_count <= 32); let mut res = fx.bcx.ins().iconst(types::I32, 0); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index df8aa1b3e6..8946ac43bc 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -171,27 +171,6 @@ macro validate_simd_type($fx:ident, $intrinsic:ident, $span:ident, $ty:expr) { } } -fn lane_type_and_count<'tcx>( - tcx: TyCtxt<'tcx>, - layout: TyAndLayout<'tcx>, -) -> (TyAndLayout<'tcx>, u16) { - assert!(layout.ty.is_simd()); - let lane_count = match layout.fields { - rustc_target::abi::FieldsShape::Array { stride: _, count } => u16::try_from(count).unwrap(), - _ => unreachable!("lane_type_and_count({:?})", layout), - }; - let lane_layout = layout - .field( - &ty::layout::LayoutCx { - tcx, - param_env: ParamEnv::reveal_all(), - }, - 0, - ) - .unwrap(); - (lane_layout, lane_count) -} - pub(crate) fn clif_vector_type<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> Option { let (element, count) = match &layout.abi { Abi::Vector { element, count } => (element.clone(), *count), @@ -218,8 +197,10 @@ fn simd_for_each_lane<'tcx, M: Module>( ) { let layout = val.layout(); - let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, layout); - let (ret_lane_layout, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); + let ret_lane_layout = fx.layout_of(ret_lane_ty); assert_eq!(lane_count, ret_lane_count); for lane_idx in 0..lane_count { @@ -248,8 +229,10 @@ fn simd_pair_for_each_lane<'tcx, M: Module>( assert_eq!(x.layout(), y.layout()); let layout = x.layout(); - let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, layout); - let (ret_lane_layout, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); + let ret_lane_layout = fx.layout_of(ret_lane_ty); assert_eq!(lane_count, ret_lane_count); for lane in 0..lane_count { @@ -269,13 +252,14 @@ fn simd_reduce<'tcx, M: Module>( ret: CPlace<'tcx>, f: impl Fn(&mut FunctionCx<'_, 'tcx, M>, TyAndLayout<'tcx>, Value, Value) -> Value, ) { - let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, val.layout()); + let (lane_count, lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); assert_eq!(lane_layout, ret.layout()); let mut res_val = val.value_field(fx, mir::Field::new(0)).load_scalar(fx); for lane_idx in 1..lane_count { let lane = val - .value_field(fx, mir::Field::new(lane_idx.into())) + .value_field(fx, mir::Field::new(lane_idx.try_into().unwrap())) .load_scalar(fx); res_val = f(fx, lane_layout, res_val, lane); } @@ -289,14 +273,14 @@ fn simd_reduce_bool<'tcx, M: Module>( ret: CPlace<'tcx>, f: impl Fn(&mut FunctionCx<'_, 'tcx, M>, Value, Value) -> Value, ) { - let (_lane_layout, lane_count) = lane_type_and_count(fx.tcx, val.layout()); + let (lane_count, _lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx); assert!(ret.layout().ty.is_bool()); let res_val = val.value_field(fx, mir::Field::new(0)).load_scalar(fx); let mut res_val = fx.bcx.ins().band_imm(res_val, 1); // mask to boolean for lane_idx in 1..lane_count { let lane = val - .value_field(fx, mir::Field::new(lane_idx.into())) + .value_field(fx, mir::Field::new(lane_idx.try_into().unwrap())) .load_scalar(fx); let lane = fx.bcx.ins().band_imm(lane, 1); // mask to boolean res_val = f(fx, res_val, lane); @@ -460,9 +444,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( "abort" => { trap_abort(fx, "Called intrinsic::abort."); } - "unreachable" => { - trap_unreachable(fx, "[corruption] Called intrinsic::unreachable."); - } "transmute" => { crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", span); } @@ -575,12 +556,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( fx.bcx.call_memmove(fx.cx.module.target_config(), dst, src, byte_amount); } }; - discriminant_value, (c ptr) { - let pointee_layout = fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty); - let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), pointee_layout); - let discr = crate::discriminant::codegen_get_discriminant(fx, val, ret.layout()); - ret.write_cvalue(fx, discr); - }; size_of_val, (c ptr) { let layout = fx.layout_of(T); let size = if layout.is_unsized() { @@ -641,22 +616,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( ); ret.write_cvalue(fx, res); }; - _ if intrinsic.starts_with("wrapping_"), (c x, c y) { - assert_eq!(x.layout().ty, y.layout().ty); - let bin_op = match intrinsic { - "wrapping_add" => BinOp::Add, - "wrapping_sub" => BinOp::Sub, - "wrapping_mul" => BinOp::Mul, - _ => unreachable!("intrinsic {}", intrinsic), - }; - let res = crate::num::codegen_int_binop( - fx, - bin_op, - x, - y, - ); - ret.write_cvalue(fx, res); - }; _ if intrinsic.starts_with("saturating_"), (c lhs, c rhs) { assert_eq!(lhs.layout().ty, rhs.layout().ty); let bin_op = match intrinsic { @@ -865,7 +824,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( } ty => unreachable!("bswap {}", ty), } - }; + } let res = CValue::by_val(swap(&mut fx.bcx, arg), fx.layout_of(T)); ret.write_cvalue(fx, res); }; @@ -916,7 +875,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( dest.write_cvalue(fx, val); }; - size_of | pref_align_of | min_align_of | needs_drop | type_id | type_name | variant_count, () { + pref_align_of | min_align_of | needs_drop | type_id | type_name | variant_count, () { let const_val = fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap(); let val = crate::constant::codegen_const_value( diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index 2b32e866e5..e0eb5c5959 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -73,11 +73,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( assert_eq!(x.layout(), y.layout()); let layout = x.layout(); - let (lane_type, lane_count) = lane_type_and_count(fx.tcx, layout); - let (ret_lane_type, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); - assert_eq!(lane_type, ret_lane_type); - assert_eq!(n, ret_lane_count); + assert_eq!(lane_ty, ret_lane_ty); + assert_eq!(u64::from(n), ret_lane_count); let total_len = lane_count * 2; @@ -105,14 +105,14 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; for &idx in &indexes { - assert!(idx < total_len, "idx {} out of range 0..{}", idx, total_len); + assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len); } for (out_idx, in_idx) in indexes.into_iter().enumerate() { - let in_lane = if in_idx < lane_count { + let in_lane = if u64::from(in_idx) < lane_count { x.value_field(fx, mir::Field::new(in_idx.into())) } else { - y.value_field(fx, mir::Field::new((in_idx - lane_count).into())) + y.value_field(fx, mir::Field::new(usize::from(in_idx) - usize::try_from(lane_count).unwrap())) }; let out_lane = ret.place_field(fx, mir::Field::new(out_idx)); out_lane.write_cvalue(fx, in_lane); @@ -131,7 +131,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); - let (_lane_type, lane_count) = lane_type_and_count(fx.tcx, base.layout()); + let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx); if idx >= lane_count.into() { fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count)); } @@ -160,7 +160,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }; let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); - let (_lane_type, lane_count) = lane_type_and_count(fx.tcx, v.layout()); + let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx); if idx >= lane_count.into() { fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count)); } @@ -212,12 +212,13 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( assert_eq!(a.layout(), c.layout()); let layout = a.layout(); - let (_lane_layout, lane_count) = lane_type_and_count(fx.tcx, layout); - let (ret_lane_layout, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); assert_eq!(lane_count, ret_lane_count); + let ret_lane_layout = fx.layout_of(ret_lane_ty); for lane in 0..lane_count { - let lane = mir::Field::new(lane.into()); + let lane = mir::Field::new(lane.try_into().unwrap()); let a_lane = a.value_field(fx, lane).load_scalar(fx); let b_lane = b.value_field(fx, lane).load_scalar(fx); let c_lane = c.value_field(fx, lane).load_scalar(fx); diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index ba9ee0d450..170750461c 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -5,7 +5,8 @@ associated_type_bounds, never_type, try_blocks, - hash_drain_filter + hash_drain_filter, + str_split_once )] #![warn(rust_2018_idioms)] #![warn(unused_lifetimes)] @@ -26,7 +27,6 @@ extern crate rustc_incremental; extern crate rustc_index; extern crate rustc_session; extern crate rustc_span; -extern crate rustc_symbol_mangling; extern crate rustc_target; // This prevents duplicating functions and statics that are already part of the host rustc process. @@ -34,6 +34,7 @@ extern crate rustc_target; extern crate rustc_driver; use std::any::Any; +use std::str::FromStr; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::CodegenResults; @@ -81,7 +82,6 @@ mod vtable; mod prelude { pub(crate) use std::convert::{TryFrom, TryInto}; - pub(crate) use rustc_ast::ast::{FloatTy, IntTy, UintTy}; pub(crate) use rustc_span::Span; pub(crate) use rustc_hir::def_id::{DefId, LOCAL_CRATE}; @@ -89,7 +89,8 @@ mod prelude { pub(crate) use rustc_middle::mir::{self, *}; pub(crate) use rustc_middle::ty::layout::{self, TyAndLayout}; pub(crate) use rustc_middle::ty::{ - self, FnSig, Instance, InstanceDef, ParamEnv, Ty, TyCtxt, TypeAndMut, TypeFoldable, + self, FloatTy, Instance, InstanceDef, IntTy, ParamEnv, Ty, TyCtxt, TypeAndMut, + TypeFoldable, UintTy, }; pub(crate) use rustc_target::abi::{Abi, LayoutOf, Scalar, Size, VariantIdx}; @@ -141,8 +142,8 @@ struct CodegenCx<'tcx, M: Module> { } impl<'tcx, M: Module> CodegenCx<'tcx, M> { - fn new(tcx: TyCtxt<'tcx>, module: M, debug_info: bool) -> Self { - let unwind_context = UnwindContext::new(tcx, module.isa()); + fn new(tcx: TyCtxt<'tcx>, module: M, debug_info: bool, pic_eh_frame: bool) -> Self { + let unwind_context = UnwindContext::new(tcx, module.isa(), pic_eh_frame); let debug_context = if debug_info { Some(DebugContext::new(tcx, module.isa())) } else { @@ -172,12 +173,55 @@ impl<'tcx, M: Module> CodegenCx<'tcx, M> { } #[derive(Copy, Clone, Debug)] +pub enum CodegenMode { + Aot, + Jit, + JitLazy, +} + +impl Default for CodegenMode { + fn default() -> Self { + CodegenMode::Aot + } +} + +impl FromStr for CodegenMode { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "aot" => Ok(CodegenMode::Aot), + "jit" => Ok(CodegenMode::Jit), + "jit-lazy" => Ok(CodegenMode::JitLazy), + _ => Err(format!("Unknown codegen mode `{}`", s)), + } + } +} + +#[derive(Copy, Clone, Debug, Default)] pub struct BackendConfig { - pub use_jit: bool, + pub codegen_mode: CodegenMode, +} + +impl BackendConfig { + fn from_opts(opts: &[String]) -> Result { + let mut config = BackendConfig::default(); + for opt in opts { + if let Some((name, value)) = opt.split_once('=') { + match name { + "mode" => config.codegen_mode = value.parse()?, + _ => return Err(format!("Unknown option `{}`", name)), + } + } else { + return Err(format!("Invalid option `{}`", opt)); + } + } + Ok(config) + } } pub struct CraneliftCodegenBackend { - pub config: BackendConfig, + pub config: Option, } impl CodegenBackend for CraneliftCodegenBackend { @@ -204,9 +248,13 @@ impl CodegenBackend for CraneliftCodegenBackend { metadata: EncodedMetadata, need_metadata_module: bool, ) -> Box { - let res = driver::codegen_crate(tcx, metadata, need_metadata_module, self.config); - - rustc_symbol_mangling::test::report_symbol_names(tcx); + let config = if let Some(config) = self.config { + config + } else { + BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) + .unwrap_or_else(|err| tcx.sess.fatal(&err)) + }; + let res = driver::codegen_crate(tcx, metadata, need_metadata_module, config); res } @@ -229,18 +277,14 @@ impl CodegenBackend for CraneliftCodegenBackend { ) -> Result<(), ErrorReported> { use rustc_codegen_ssa::back::link::link_binary; - let _timer = sess.prof.generic_activity("link_crate"); - - sess.time("linking", || { - let target_cpu = crate::target_triple(sess).to_string(); - link_binary::>( - sess, - &codegen_results, - outputs, - &codegen_results.crate_name.as_str(), - &target_cpu, - ); - }); + let target_cpu = crate::target_triple(sess).to_string(); + link_binary::>( + sess, + &codegen_results, + outputs, + &codegen_results.crate_name.as_str(), + &target_cpu, + ); Ok(()) } @@ -250,17 +294,13 @@ fn target_triple(sess: &Session) -> target_lexicon::Triple { sess.target.llvm_target.parse().unwrap() } -fn build_isa(sess: &Session, enable_pic: bool) -> Box { +fn build_isa(sess: &Session) -> Box { use target_lexicon::BinaryFormat; let target_triple = crate::target_triple(sess); let mut flags_builder = settings::builder(); - if enable_pic { - flags_builder.enable("is_pic").unwrap(); - } else { - flags_builder.set("is_pic", "false").unwrap(); - } + flags_builder.enable("is_pic").unwrap(); flags_builder.set("enable_probestack", "false").unwrap(); // __cranelift_probestack is not provided flags_builder .set( @@ -283,8 +323,6 @@ fn build_isa(sess: &Session, enable_pic: bool) -> Box { @@ -297,11 +335,16 @@ fn build_isa(sess: &Session, enable_pic: bool) -> Box { sess.warn("Optimizing for size is not supported. Just ignoring the request"); } - }*/ + } let flags = settings::Flags::new(flags_builder); - let mut isa_builder = cranelift_codegen::isa::lookup(target_triple).unwrap(); + let variant = if cfg!(feature = "oldbe") { + cranelift_codegen::isa::BackendVariant::Legacy + } else { + cranelift_codegen::isa::BackendVariant::MachInst + }; + let mut isa_builder = cranelift_codegen::isa::lookup_variant(target_triple, variant).unwrap(); // Don't use "haswell", as it implies `has_lzcnt`.macOS CI is still at Ivy Bridge EP, so `lzcnt` // is interpreted as `bsr`. isa_builder.enable("nehalem").unwrap(); @@ -311,7 +354,5 @@ fn build_isa(sess: &Session, enable_pic: bool) -> Box Box { - Box::new(CraneliftCodegenBackend { - config: BackendConfig { use_jit: false }, - }) + Box::new(CraneliftCodegenBackend { config: None }) } diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index 6c472e6774..b193cea877 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -69,8 +69,8 @@ pub(crate) fn maybe_create_entry_wrapper( let instance = Instance::mono(tcx, rust_main_def_id).polymorphize(tcx); - let (main_name, main_sig) = - get_function_name_and_sig(tcx, m.isa().triple(), instance, false); + let main_name = tcx.symbol_name(instance).name.to_string(); + let main_sig = get_function_sig(tcx, m.isa().triple(), instance); let main_func_id = m .declare_function(&main_name, Linkage::Import, &main_sig) .unwrap(); diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index 41f4a9b966..d1d2b3b872 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -280,7 +280,6 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( (val, fx.bcx.ins().bor(has_underflow, has_overflow)) } types::I64 => { - //let val = fx.easy_call("__mulodi4", &[lhs, rhs, overflow_ptr], types::I64); let val = fx.bcx.ins().imul(lhs, rhs); let has_overflow = if !signed { let val_hi = fx.bcx.ins().umulhi(lhs, rhs); diff --git a/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs b/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs index f8e0f3af3d..a575ed8dc3 100644 --- a/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs +++ b/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs @@ -73,7 +73,7 @@ pub(crate) fn make_branchable_value(bcx: &mut FunctionBuilder<'_>, arg: Value) - })() .unwrap_or_else(|| { match bcx.func.dfg.value_type(arg) { - types::I8 | types::I32 => { + types::I8 | types::I16 => { // WORKAROUND for brz.i8 and brnz.i8 not yet being implemented bcx.ins().uextend(types::I32, arg) } @@ -81,3 +81,40 @@ pub(crate) fn make_branchable_value(bcx: &mut FunctionBuilder<'_>, arg: Value) - } }) } + +/// Returns whether the branch is statically known to be taken or `None` if it isn't statically known. +pub(crate) fn maybe_known_branch_taken( + bcx: &FunctionBuilder<'_>, + arg: Value, + test_zero: bool, +) -> Option { + let arg_inst = if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) { + arg_inst + } else { + return None; + }; + + match bcx.func.dfg[arg_inst] { + InstructionData::UnaryBool { + opcode: Opcode::Bconst, + imm, + } => { + if test_zero { + Some(!imm) + } else { + Some(imm) + } + } + InstructionData::UnaryImm { + opcode: Opcode::Iconst, + imm, + } => { + if test_zero { + Some(imm.bits() == 0) + } else { + Some(imm.bits() != 0) + } + } + _ => None, + } +} diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs index a9f060e51d..f4a15ab12d 100644 --- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs +++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs @@ -53,6 +53,7 @@ //! ``` use std::fmt; +use std::io::Write; use cranelift_codegen::{ entity::SecondaryMap, @@ -60,7 +61,9 @@ use cranelift_codegen::{ write::{FuncWriter, PlainWriter}, }; +use rustc_middle::ty::layout::FnAbiExt; use rustc_session::config::OutputType; +use rustc_target::abi::call::FnAbi; use crate::prelude::*; @@ -77,11 +80,8 @@ impl CommentWriter { format!("symbol {}", tcx.symbol_name(instance).name), format!("instance {:?}", instance), format!( - "sig {:?}", - tcx.normalize_erasing_late_bound_regions( - ParamEnv::reveal_all(), - crate::abi::fn_sig_for_fn_abi(tcx, instance) - ) + "abi {:?}", + FnAbi::of_instance(&RevealAllLayoutCx(tcx), instance, &[]) ), String::new(), ] @@ -200,32 +200,24 @@ impl FunctionCx<'_, '_, M> { } } -pub(crate) fn write_clif_file<'tcx>( - tcx: TyCtxt<'tcx>, - postfix: &str, - isa: Option<&dyn cranelift_codegen::isa::TargetIsa>, - instance: Instance<'tcx>, - context: &cranelift_codegen::Context, - mut clif_comments: &CommentWriter, -) { - use std::io::Write; - - if !cfg!(debug_assertions) - && !tcx +pub(crate) fn should_write_ir(tcx: TyCtxt<'_>) -> bool { + cfg!(debug_assertions) + || tcx .sess .opts .output_types .contains_key(&OutputType::LlvmAssembly) - { +} + +pub(crate) fn write_ir_file<'tcx>( + tcx: TyCtxt<'tcx>, + name: &str, + write: impl FnOnce(&mut dyn Write) -> std::io::Result<()>, +) { + if !should_write_ir(tcx) { return; } - let value_ranges = isa.map(|isa| { - context - .build_value_labels_ranges(isa) - .expect("value location ranges") - }); - let clif_output_dir = tcx.output_filenames(LOCAL_CRATE).with_extension("clif"); match std::fs::create_dir(&clif_output_dir) { @@ -234,41 +226,58 @@ pub(crate) fn write_clif_file<'tcx>( res @ Err(_) => res.unwrap(), } - let clif_file_name = clif_output_dir.join(format!( - "{}.{}.clif", - tcx.symbol_name(instance).name, - postfix - )); - - let mut clif = String::new(); - cranelift_codegen::write::decorate_function( - &mut clif_comments, - &mut clif, - &context.func, - &DisplayFunctionAnnotations { - isa: Some(&*crate::build_isa( - tcx.sess, true, /* PIC doesn't matter here */ - )), - value_ranges: value_ranges.as_ref(), - }, - ) - .unwrap(); + let clif_file_name = clif_output_dir.join(name); let res: std::io::Result<()> = try { let mut file = std::fs::File::create(clif_file_name)?; - let target_triple = crate::target_triple(tcx.sess); - writeln!(file, "test compile")?; - writeln!(file, "set is_pic")?; - writeln!(file, "set enable_simd")?; - writeln!(file, "target {} haswell", target_triple)?; - writeln!(file)?; - file.write_all(clif.as_bytes())?; + write(&mut file)?; }; if let Err(err) = res { - tcx.sess.warn(&format!("err writing clif file: {}", err)); + tcx.sess.warn(&format!("error writing ir file: {}", err)); } } +pub(crate) fn write_clif_file<'tcx>( + tcx: TyCtxt<'tcx>, + postfix: &str, + isa: Option<&dyn cranelift_codegen::isa::TargetIsa>, + instance: Instance<'tcx>, + context: &cranelift_codegen::Context, + mut clif_comments: &CommentWriter, +) { + write_ir_file( + tcx, + &format!("{}.{}.clif", tcx.symbol_name(instance).name, postfix), + |file| { + let value_ranges = isa.map(|isa| { + context + .build_value_labels_ranges(isa) + .expect("value location ranges") + }); + + let mut clif = String::new(); + cranelift_codegen::write::decorate_function( + &mut clif_comments, + &mut clif, + &context.func, + &DisplayFunctionAnnotations { + isa: Some(&*crate::build_isa(tcx.sess)), + value_ranges: value_ranges.as_ref(), + }, + ) + .unwrap(); + + writeln!(file, "test compile")?; + writeln!(file, "set is_pic")?; + writeln!(file, "set enable_simd")?; + writeln!(file, "target {} haswell", crate::target_triple(tcx.sess))?; + writeln!(file)?; + file.write_all(clif.as_bytes())?; + Ok(()) + }, + ); +} + impl fmt::Debug for FunctionCx<'_, '_, M> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "{:?}", self.instance.substs)?; diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 5bcb11fd51..765604e0f9 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -334,7 +334,9 @@ impl<'tcx> CPlace<'tcx> { let stack_slot = fx.bcx.create_stack_slot(StackSlotData { kind: StackSlotKind::ExplicitSlot, - size: u32::try_from(layout.size.bytes()).unwrap(), + // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to + // specify stack slot alignment. + size: (u32::try_from(layout.size.bytes()).unwrap() + 15) / 16 * 16, offset: None, }); CPlace { @@ -450,64 +452,6 @@ impl<'tcx> CPlace<'tcx> { fx: &mut FunctionCx<'_, 'tcx, impl Module>, from: CValue<'tcx>, ) { - fn assert_assignable<'tcx>( - fx: &FunctionCx<'_, 'tcx, impl Module>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, - ) { - match (from_ty.kind(), to_ty.kind()) { - (ty::Ref(_, a, _), ty::Ref(_, b, _)) - | ( - ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), - ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }), - ) => { - assert_assignable(fx, a, b); - } - (ty::FnPtr(_), ty::FnPtr(_)) => { - let from_sig = fx.tcx.normalize_erasing_late_bound_regions( - ParamEnv::reveal_all(), - from_ty.fn_sig(fx.tcx), - ); - let to_sig = fx.tcx.normalize_erasing_late_bound_regions( - ParamEnv::reveal_all(), - to_ty.fn_sig(fx.tcx), - ); - assert_eq!( - from_sig, to_sig, - "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}", - from_sig, to_sig, fx, - ); - // fn(&T) -> for<'l> fn(&'l T) is allowed - } - (&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => { - for (from, to) in from_traits.iter().zip(to_traits) { - let from = fx - .tcx - .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from); - let to = fx - .tcx - .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to); - assert_eq!( - from, to, - "Can't write trait object of incompatible traits {:?} to place with traits {:?}\n\n{:#?}", - from_traits, to_traits, fx, - ); - } - // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed - } - _ => { - assert_eq!( - from_ty, - to_ty, - "Can't write value with incompatible type {:?} to place with type {:?}\n\n{:#?}", - from_ty, - to_ty, - fx, - ); - } - } - } - assert_assignable(fx, from.layout().ty, self.layout().ty); self.write_cvalue_maybe_transmute(fx, from, "write_cvalue"); @@ -556,7 +500,9 @@ impl<'tcx> CPlace<'tcx> { // FIXME do something more efficient for transmutes between vectors and integers. let stack_slot = fx.bcx.create_stack_slot(StackSlotData { kind: StackSlotKind::ExplicitSlot, - size: src_ty.bytes(), + // FIXME Don't force the size to a multiple of 16 bytes once Cranelift gets a way to + // specify stack slot alignment. + size: (src_ty.bytes() + 15) / 16 * 16, offset: None, }); let ptr = Pointer::stack_slot(stack_slot); @@ -794,3 +740,62 @@ impl<'tcx> CPlace<'tcx> { } } } + +#[track_caller] +pub(crate) fn assert_assignable<'tcx>( + fx: &FunctionCx<'_, 'tcx, impl Module>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) { + match (from_ty.kind(), to_ty.kind()) { + (ty::Ref(_, a, _), ty::Ref(_, b, _)) + | ( + ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), + ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }), + ) => { + assert_assignable(fx, a, b); + } + (ty::Ref(_, a, _), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ })) + | (ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::Ref(_, b, _)) => { + assert_assignable(fx, a, b); + } + (ty::FnPtr(_), ty::FnPtr(_)) => { + let from_sig = fx.tcx.normalize_erasing_late_bound_regions( + ParamEnv::reveal_all(), + from_ty.fn_sig(fx.tcx), + ); + let to_sig = fx + .tcx + .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to_ty.fn_sig(fx.tcx)); + assert_eq!( + from_sig, to_sig, + "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}", + from_sig, to_sig, fx, + ); + // fn(&T) -> for<'l> fn(&'l T) is allowed + } + (&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => { + for (from, to) in from_traits.iter().zip(to_traits) { + let from = fx + .tcx + .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from); + let to = fx + .tcx + .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to); + assert_eq!( + from, to, + "Can't write trait object of incompatible traits {:?} to place with traits {:?}\n\n{:#?}", + from_traits, to_traits, fx, + ); + } + // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed + } + _ => { + assert_eq!( + from_ty, to_ty, + "Can't write value with incompatible type {:?} to place with type {:?}\n\n{:#?}", + from_ty, to_ty, fx, + ); + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index 238abc0d8b..8f15586a9d 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -158,7 +158,8 @@ fn build_vtable<'tcx>( ) .unwrap(); - fx.cx.module.define_data(data_id, &data_ctx).unwrap(); + // FIXME don't duplicate definitions in lazy jit mode + let _ = fx.cx.module.define_data(data_id, &data_ctx); data_id } diff --git a/compiler/rustc_codegen_cranelift/test.sh b/compiler/rustc_codegen_cranelift/test.sh index c6c4956e48..5ab10e0e90 100755 --- a/compiler/rustc_codegen_cranelift/test.sh +++ b/compiler/rustc_codegen_cranelift/test.sh @@ -1,9 +1,7 @@ #!/bin/bash set -e -export RUSTFLAGS="-Zrun_dsymutil=no" - -./build.sh --without-sysroot "$@" +./build.sh --sysroot none "$@" rm -r target/out || true diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 915dd3d9ed..a69241e456 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -389,7 +389,7 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> { fn llvm_cconv(&self) -> llvm::CallConv { match self.conv { - Conv::C | Conv::Rust => llvm::CCallConv, + Conv::C | Conv::Rust | Conv::CCmseNonSecureCall => llvm::CCallConv, Conv::AmdGpuKernel => llvm::AmdGpuKernel, Conv::AvrInterrupt => llvm::AvrInterrupt, Conv::AvrNonBlockingInterrupt => llvm::AvrNonBlockingInterrupt, @@ -546,6 +546,18 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> { if cconv != llvm::CCallConv { llvm::SetInstructionCallConv(callsite, cconv); } + + if self.conv == Conv::CCmseNonSecureCall { + // This will probably get ignored on all targets but those supporting the TrustZone-M + // extension (thumbv8m targets). + unsafe { + llvm::AddCallSiteAttrString( + callsite, + llvm::AttributePlace::Function, + rustc_data_structures::const_cstr!("cmse_nonsecure_call"), + ); + } + } } } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 97c38e04bc..a78d692aaa 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::{OptLevel, SanitizerSet}; use rustc_session::Session; +use rustc_target::spec::StackProbeType; use crate::attributes; use crate::llvm::AttributePlace::Function; @@ -98,12 +99,6 @@ fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { } fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { - // Only use stack probes if the target specification indicates that we - // should be using stack probes - if !cx.sess().target.stack_probes { - return; - } - // Currently stack probes seem somewhat incompatible with the address // sanitizer and thread sanitizer. With asan we're already protected from // stack overflow anyway so we don't really need stack probes regardless. @@ -127,14 +122,31 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { return; } - // Flag our internal `__rust_probestack` function as the stack probe symbol. - // This is defined in the `compiler-builtins` crate for each architecture. - llvm::AddFunctionAttrStringValue( - llfn, - llvm::AttributePlace::Function, - const_cstr!("probe-stack"), - const_cstr!("__rust_probestack"), - ); + let attr_value = match cx.sess().target.stack_probes { + StackProbeType::None => None, + // Request LLVM to generate the probes inline. If the given LLVM version does not support + // this, no probe is generated at all (even if the attribute is specified). + StackProbeType::Inline => Some(const_cstr!("inline-asm")), + // Flag our internal `__rust_probestack` function as the stack probe symbol. + // This is defined in the `compiler-builtins` crate for each architecture. + StackProbeType::Call => Some(const_cstr!("__rust_probestack")), + // Pick from the two above based on the LLVM version. + StackProbeType::InlineOrCall { min_llvm_version_for_inline } => { + if llvm_util::get_version() < min_llvm_version_for_inline { + Some(const_cstr!("__rust_probestack")) + } else { + Some(const_cstr!("inline-asm")) + } + } + }; + if let Some(attr_value) = attr_value { + llvm::AddFunctionAttrStringValue( + llfn, + llvm::AttributePlace::Function, + const_cstr!("probe-stack"), + attr_value, + ); + } } pub fn llvm_target_features(sess: &Session) -> impl Iterator { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 29415973ed..5effe68752 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -732,10 +732,7 @@ pub unsafe fn optimize_thin_module( let diag_handler = cgcx.create_diag_handler(); let module_name = &thin_module.shared.module_names[thin_module.idx]; - let split_dwarf_file = cgcx - .output_filenames - .split_dwarf_filename(cgcx.split_dwarf_kind, Some(module_name.to_str().unwrap())); - let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file }; + let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap()); let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&diag_handler, &e))?; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 3fda1e26da..326ae354cc 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -23,13 +23,11 @@ use rustc_fs_util::{link_or_copy, path_to_c_string}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{ - self, Lto, OutputType, Passes, SanitizerSet, SplitDwarfKind, SwitchWithOptPath, -}; +use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::InnerSpan; -use rustc_target::spec::{CodeModel, RelocModel}; +use rustc_target::spec::{CodeModel, RelocModel, SplitDebuginfo}; use tracing::debug; use libc::{c_char, c_int, c_uint, c_void, size_t}; @@ -93,9 +91,12 @@ pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm: } pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut llvm::TargetMachine { - let split_dwarf_file = tcx - .output_filenames(LOCAL_CRATE) - .split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(mod_name)); + let split_dwarf_file = if tcx.sess.target_can_use_split_dwarf() { + tcx.output_filenames(LOCAL_CRATE) + .split_dwarf_filename(tcx.sess.split_debuginfo(), Some(mod_name)) + } else { + None + }; let config = TargetMachineFactoryConfig { split_dwarf_file }; target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE))(config) .unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise()) @@ -164,7 +165,8 @@ pub fn target_machine_factory( let code_model = to_llvm_code_model(sess.code_model()); - let features = attributes::llvm_target_features(sess).collect::>(); + let mut features = llvm_util::handle_native_features(sess); + features.extend(attributes::llvm_target_features(sess).map(|s| s.to_owned())); let mut singlethread = sess.target.singlethread; // On the wasm target once the `atomics` feature is enabled that means that @@ -485,7 +487,7 @@ pub(crate) unsafe fn optimize( diag_handler: &Handler, module: &ModuleCodegen, config: &ModuleConfig, -) -> Result<(), FatalError> { +) { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_optimize", &module.name[..]); let llmod = module.module_llvm.llmod(); @@ -511,7 +513,7 @@ pub(crate) unsafe fn optimize( _ => llvm::OptStage::PreLinkNoLTO, }; optimize_with_new_llvm_pass_manager(cgcx, module, config, opt_level, opt_stage); - return Ok(()); + return; } if cgcx.prof.llvm_recording_enabled() { @@ -634,7 +636,6 @@ pub(crate) unsafe fn optimize( llvm::LLVMDisposePassManager(fpm); llvm::LLVMDisposePassManager(mpm); } - Ok(()) } unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static mut llvm::Pass>) { @@ -838,11 +839,17 @@ pub(crate) unsafe fn codegen( .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]); let dwo_out = cgcx.output_filenames.temp_path_dwo(module_name); - let dwo_out = match cgcx.split_dwarf_kind { + let dwo_out = match cgcx.split_debuginfo { // Don't change how DWARF is emitted in single mode (or when disabled). - SplitDwarfKind::None | SplitDwarfKind::Single => None, + SplitDebuginfo::Off | SplitDebuginfo::Packed => None, // Emit (a subset of the) DWARF into a separate file in split mode. - SplitDwarfKind::Split => Some(dwo_out.as_path()), + SplitDebuginfo::Unpacked => { + if cgcx.target_can_use_split_dwarf { + Some(dwo_out.as_path()) + } else { + None + } + } }; with_codegen(tm, llmod, config.no_builtins, |cpm| { @@ -880,7 +887,7 @@ pub(crate) unsafe fn codegen( Ok(module.into_compiled_module( config.emit_obj != EmitObj::None, - cgcx.split_dwarf_kind == SplitDwarfKind::Split, + cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked, config.emit_bc, &cgcx.output_filenames, )) @@ -1002,8 +1009,7 @@ pub unsafe fn with_llvm_pmb( // reasonable defaults and prepare it to actually populate the pass // manager. let builder = llvm::LLVMPassManagerBuilderCreate(); - let opt_size = - config.opt_size.map(|x| to_llvm_opt_settings(x).1).unwrap_or(llvm::CodeGenOptSizeNone); + let opt_size = config.opt_size.map_or(llvm::CodeGenOptSizeNone, |x| to_llvm_opt_settings(x).1); let inline_threshold = config.inline_threshold; let pgo_gen_path = get_pgo_gen_path(config); let pgo_use_path = get_pgo_use_path(config); diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 7d01f6a549..d5be3132de 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -1,17 +1,15 @@ -//! Codegen the completed AST to the LLVM IR. -//! -//! Some functions here, such as codegen_block and codegen_expr, return a value -- -//! the result of the codegen to LLVM -- while others, such as codegen_fn -//! and mono_item, are called only for the side effect of adding a -//! particular definition to the LLVM IR output we're producing. +//! Codegen the MIR to the LLVM IR. //! //! Hopefully useful general knowledge about codegen: //! -//! * There's no way to find out the `Ty` type of a Value. Doing so +//! * There's no way to find out the [`Ty`] type of a [`Value`]. Doing so //! would be "trying to get the eggs out of an omelette" (credit: -//! pcwalton). You can, instead, find out its `llvm::Type` by calling `val_ty`, -//! but one `llvm::Type` corresponds to many `Ty`s; for instance, `tup(int, int, -//! int)` and `rec(x=int, y=int, z=int)` will have the same `llvm::Type`. +//! pcwalton). You can, instead, find out its [`llvm::Type`] by calling [`val_ty`], +//! but one [`llvm::Type`] corresponds to many [`Ty`]s; for instance, `tup(int, int, +//! int)` and `rec(x=int, y=int, z=int)` will have the same [`llvm::Type`]. +//! +//! [`Ty`]: rustc_middle::ty::Ty +//! [`val_ty`]: common::val_ty use super::ModuleLlvm; diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index f122fa14e7..d2f4d3edc2 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -304,9 +304,8 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { lhs: Self::Value, rhs: Self::Value, ) -> (Self::Value, Self::Value) { - use rustc_ast::IntTy::*; - use rustc_ast::UintTy::*; use rustc_middle::ty::{Int, Uint}; + use rustc_middle::ty::{IntTy::*, UintTy::*}; let new_kind = match ty.kind() { Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)), diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 34e1b7a604..58af9d4cd0 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -314,6 +314,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } +/// Get the [LLVM type][Type] of a [`Value`]. pub fn val_ty(v: &Value) -> &Type { unsafe { llvm::LLVMTypeOf(v) } } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 14dd245625..16e1a8a124 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -8,9 +8,7 @@ use crate::value::Value; use libc::c_uint; use rustc_codegen_ssa::traits::*; use rustc_data_structures::const_cstr; -use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::Node; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ read_target_uint, Allocation, ErrorHandled, GlobalAlloc, Pointer, @@ -18,7 +16,6 @@ use rustc_middle::mir::interpret::{ use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, span_bug}; -use rustc_span::symbol::sym; use rustc_target::abi::{AddressSpace, Align, HasDataLayout, LayoutOf, Primitive, Scalar, Size}; use tracing::debug; @@ -209,70 +206,42 @@ impl CodegenCx<'ll, 'tcx> { let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); let sym = self.tcx.symbol_name(instance).name; + let fn_attrs = self.tcx.codegen_fn_attrs(def_id); - debug!("get_static: sym={} instance={:?}", sym, instance); + debug!("get_static: sym={} instance={:?} fn_attrs={:?}", sym, instance, fn_attrs); - let g = if let Some(local_def_id) = def_id.as_local() { - let id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); + let g = if def_id.is_local() && !self.tcx.is_foreign_item(def_id) { let llty = self.layout_of(ty).llvm_type(self); - // FIXME: refactor this to work without accessing the HIR - let (g, attrs) = match self.tcx.hir().get(id) { - Node::Item(&hir::Item { attrs, kind: hir::ItemKind::Static(..), .. }) => { - if let Some(g) = self.get_declared_value(sym) { - if self.val_ty(g) != self.type_ptr_to(llty) { - span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); - } - } - - let g = self.declare_global(sym, llty); - - if !self.tcx.is_reachable_non_generic(local_def_id) { - unsafe { - llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden); - } - } - - (g, attrs) + if let Some(g) = self.get_declared_value(sym) { + if self.val_ty(g) != self.type_ptr_to(llty) { + span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); } + } - Node::ForeignItem(&hir::ForeignItem { - ref attrs, - kind: hir::ForeignItemKind::Static(..), - .. - }) => { - let fn_attrs = self.tcx.codegen_fn_attrs(local_def_id); - (check_and_apply_linkage(&self, &fn_attrs, ty, sym, def_id), &**attrs) - } - - item => bug!("get_static: expected static, found {:?}", item), - }; - - debug!("get_static: sym={} attrs={:?}", sym, attrs); + let g = self.declare_global(sym, llty); - for attr in attrs { - if self.tcx.sess.check_name(attr, sym::thread_local) { - llvm::set_thread_local_mode(g, self.tls_model); + if !self.tcx.is_reachable_non_generic(def_id) { + unsafe { + llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden); } } g } 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)); + check_and_apply_linkage(&self, &fn_attrs, ty, sym, def_id) + }; - let attrs = self.tcx.codegen_fn_attrs(def_id); - let g = check_and_apply_linkage(&self, &attrs, ty, sym, def_id); - - // 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. - if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { - llvm::set_thread_local_mode(g, self.tls_model); - } + // 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. + if fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { + llvm::set_thread_local_mode(g, self.tls_model); + } + if !def_id.is_local() { let needs_dll_storage_attr = self.use_dll_storage_attrs && !self.tcx.is_foreign_item(def_id) && // ThinLTO can't handle this workaround in all cases, so we don't // emit the attrs. Instead we make them unnecessary by disallowing @@ -304,8 +273,7 @@ impl CodegenCx<'ll, 'tcx> { } } } - g - }; + } if self.use_dll_storage_attrs && self.tcx.is_dllimport_foreign_item(def_id) { // For foreign (native) libs we know the exact storage type to use. diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 8dd4030807..6acd26bd41 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -114,7 +114,7 @@ pub unsafe fn create_module( let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); let mut target_data_layout = sess.target.data_layout.clone(); - if llvm_util::get_major_version() < 10 + if llvm_util::get_version() < (10, 0, 0) && (sess.target.arch == "x86" || sess.target.arch == "x86_64") { target_data_layout = strip_x86_address_spaces(target_data_layout); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 72ba5bbd5f..444a9d4ba0 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -292,7 +292,7 @@ fn add_unreachable_coverage<'tcx>( if let Some(non_codegenned_file_name) = tcx.covered_file_name(non_codegenned_def_id) { let def_ids = unreachable_def_ids_by_file .entry(*non_codegenned_file_name) - .or_insert_with(|| Vec::new()); + .or_insert_with(Vec::new); def_ids.push(non_codegenned_def_id); } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index fa285f3488..6e7c0b3e34 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -18,7 +18,6 @@ use crate::llvm::debuginfo::{ }; use crate::value::Value; -use rustc_ast as ast; use rustc_codegen_ssa::traits::*; use rustc_data_structures::const_cstr; use rustc_data_structures::fingerprint::Fingerprint; @@ -830,37 +829,37 @@ trait MsvcBasicName { fn msvc_basic_name(self) -> &'static str; } -impl MsvcBasicName for ast::IntTy { +impl MsvcBasicName for ty::IntTy { fn msvc_basic_name(self) -> &'static str { match self { - ast::IntTy::Isize => "ptrdiff_t", - ast::IntTy::I8 => "__int8", - ast::IntTy::I16 => "__int16", - ast::IntTy::I32 => "__int32", - ast::IntTy::I64 => "__int64", - ast::IntTy::I128 => "__int128", + ty::IntTy::Isize => "ptrdiff_t", + ty::IntTy::I8 => "__int8", + ty::IntTy::I16 => "__int16", + ty::IntTy::I32 => "__int32", + ty::IntTy::I64 => "__int64", + ty::IntTy::I128 => "__int128", } } } -impl MsvcBasicName for ast::UintTy { +impl MsvcBasicName for ty::UintTy { fn msvc_basic_name(self) -> &'static str { match self { - ast::UintTy::Usize => "size_t", - ast::UintTy::U8 => "unsigned __int8", - ast::UintTy::U16 => "unsigned __int16", - ast::UintTy::U32 => "unsigned __int32", - ast::UintTy::U64 => "unsigned __int64", - ast::UintTy::U128 => "unsigned __int128", + ty::UintTy::Usize => "size_t", + ty::UintTy::U8 => "unsigned __int8", + ty::UintTy::U16 => "unsigned __int16", + ty::UintTy::U32 => "unsigned __int32", + ty::UintTy::U64 => "unsigned __int64", + ty::UintTy::U128 => "unsigned __int128", } } } -impl MsvcBasicName for ast::FloatTy { +impl MsvcBasicName for ty::FloatTy { fn msvc_basic_name(self) -> &'static str { match self { - ast::FloatTy::F32 => "float", - ast::FloatTy::F64 => "double", + ty::FloatTy::F32 => "float", + ty::FloatTy::F64 => "double", } } } @@ -996,10 +995,13 @@ pub fn compile_unit_metadata( let flags = "\0"; let out_dir = &tcx.output_filenames(LOCAL_CRATE).out_directory; - let split_name = tcx - .output_filenames(LOCAL_CRATE) - .split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(codegen_unit_name)) - .unwrap_or_default(); + let split_name = if tcx.sess.target_can_use_split_dwarf() { + tcx.output_filenames(LOCAL_CRATE) + .split_dwarf_filename(tcx.sess.split_debuginfo(), Some(codegen_unit_name)) + } else { + None + } + .unwrap_or_default(); let out_dir = out_dir.to_str().unwrap(); let split_name = split_name.to_str().unwrap(); @@ -1832,8 +1834,9 @@ impl<'tcx> VariantInfo<'_, 'tcx> { fn source_info(&self, cx: &CodegenCx<'ll, 'tcx>) -> Option> { match self { VariantInfo::Generator { def_id, variant_index, .. } => { - let span = - cx.tcx.generator_layout(*def_id).variant_source_info[*variant_index].span; + let span = cx.tcx.generator_layout(*def_id).unwrap().variant_source_info + [*variant_index] + .span; if !span.is_dummy() { let loc = cx.lookup_debug_loc(span.lo()); return Some(SourceInfo { @@ -2322,13 +2325,13 @@ fn set_members_of_composite_type( DIB(cx), composite_type_metadata, Some(type_array), - type_params, + Some(type_params), ); } } /// Computes the type parameters for a type, if any, for the given metadata. -fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> Option<&'ll DIArray> { +fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> &'ll DIArray { if let ty::Adt(def, substs) = *ty.kind() { if substs.types().next().is_some() { let generics = cx.tcx.generics_of(def.did); @@ -2358,10 +2361,10 @@ fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> Option<&' }) .collect(); - return Some(create_DIArray(DIB(cx), &template_params[..])); + return create_DIArray(DIB(cx), &template_params[..]); } } - return Some(create_DIArray(DIB(cx), &[])); + return create_DIArray(DIB(cx), &[]); fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) -> Vec { let mut names = generics diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index ccbe7325cc..955e739b2c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -552,7 +552,6 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation( - utils::debug_context(self).llcontext, line.unwrap_or(UNKNOWN_LINE_NUMBER), col.unwrap_or(UNKNOWN_COLUMN_NUMBER), scope, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index a58c2fbd8a..d11c1592f9 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -160,7 +160,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { module: &ModuleCodegen, config: &ModuleConfig, ) -> Result<(), FatalError> { - back::write::optimize(cgcx, diag_handler, module, config) + Ok(back::write::optimize(cgcx, diag_handler, module, config)) } unsafe fn optimize_thin( cgcx: &CodegenContext, @@ -298,21 +298,19 @@ impl CodegenBackend for LlvmCodegenBackend { codegen_results: CodegenResults, outputs: &OutputFilenames, ) -> Result<(), ErrorReported> { + use crate::back::archive::LlvmArchiveBuilder; + use rustc_codegen_ssa::back::link::link_binary; + // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. - sess.time("link_crate", || { - use crate::back::archive::LlvmArchiveBuilder; - use rustc_codegen_ssa::back::link::link_binary; - - let target_cpu = crate::llvm_util::target_cpu(sess); - link_binary::>( - sess, - &codegen_results, - outputs, - &codegen_results.crate_name.as_str(), - target_cpu, - ); - }); + let target_cpu = crate::llvm_util::target_cpu(sess); + link_binary::>( + sess, + &codegen_results, + outputs, + &codegen_results.crate_name.as_str(), + target_cpu, + ); Ok(()) } @@ -353,12 +351,7 @@ impl ModuleLlvm { unsafe { let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); let llmod_raw = back::lto::parse_module(llcx, name, buffer, handler)?; - - let split_dwarf_file = cgcx - .output_filenames - .split_dwarf_filename(cgcx.split_dwarf_kind, Some(name.to_str().unwrap())); - let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file }; - + let tm_factory_config = TargetMachineFactoryConfig::new(&cgcx, name.to_str().unwrap()); let tm = match (cgcx.tm_factory)(tm_factory_config) { Ok(m) => m, Err(e) => { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 707aaa2b53..e82198f8f0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1100,6 +1100,7 @@ extern "C" { // Operations on call sites pub fn LLVMSetInstructionCallConv(Instr: &Value, CC: c_uint); pub fn LLVMRustAddCallSiteAttribute(Instr: &Value, index: c_uint, attr: Attribute); + pub fn LLVMRustAddCallSiteAttrString(Instr: &Value, index: c_uint, Name: *const c_char); pub fn LLVMRustAddAlignmentCallSiteAttr(Instr: &Value, index: c_uint, bytes: u32); pub fn LLVMRustAddDereferenceableCallSiteAttr(Instr: &Value, index: c_uint, bytes: u64); pub fn LLVMRustAddDereferenceableOrNullCallSiteAttr(Instr: &Value, index: c_uint, bytes: u64); @@ -1708,6 +1709,10 @@ extern "C" { PM: &PassManager<'_>, ); + pub fn LLVMGetHostCPUFeatures() -> *mut c_char; + + pub fn LLVMDisposeMessage(message: *mut c_char); + // Stuff that's in llvm-wrapper/ because it's not upstream yet. /// Opens an object file. @@ -1807,6 +1812,7 @@ extern "C" { pub fn LLVMRustDebugMetadataVersion() -> u32; pub fn LLVMRustVersionMajor() -> u32; pub fn LLVMRustVersionMinor() -> u32; + pub fn LLVMRustVersionPatch() -> u32; pub fn LLVMRustAddModuleFlag(M: &Module, name: *const c_char, value: u32); @@ -2098,7 +2104,6 @@ extern "C" { ); pub fn LLVMRustDIBuilderCreateDebugLocation( - Context: &'a Context, Line: c_uint, Column: c_uint, Scope: &'a DIScope, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index fc40065a96..bb9c6d4737 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -43,6 +43,10 @@ pub fn AddFunctionAttrString(llfn: &'a Value, idx: AttributePlace, attr: &CStr) } } +pub fn AddCallSiteAttrString(callsite: &Value, idx: AttributePlace, attr: &CStr) { + unsafe { LLVMRustAddCallSiteAttrString(callsite, idx.as_uint(), attr.as_ptr()) } +} + #[derive(Copy, Clone)] pub enum AttributePlace { ReturnValue, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index a3139ce5a3..544ef38c12 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -8,7 +8,7 @@ use rustc_session::config::PrintRequest; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::slice; use std::str; @@ -171,16 +171,17 @@ pub fn target_features(sess: &Session) -> Vec { } pub fn print_version() { + let (major, minor, patch) = get_version(); + println!("LLVM version: {}.{}.{}", major, minor, patch); +} + +pub fn get_version() -> (u32, u32, u32) { // Can be called without initializing LLVM unsafe { - println!("LLVM version: {}.{}", llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor()); + (llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor(), llvm::LLVMRustVersionPatch()) } } -pub fn get_major_version() -> u32 { - unsafe { llvm::LLVMRustVersionMajor() } -} - pub fn print_passes() { // Can be called without initializing LLVM unsafe { @@ -213,17 +214,42 @@ fn handle_native(name: &str) -> &str { } pub fn target_cpu(sess: &Session) -> &str { - let name = match sess.opts.cg.target_cpu { - Some(ref s) => &**s, - None => &*sess.target.cpu, - }; - + let name = sess.opts.cg.target_cpu.as_ref().unwrap_or(&sess.target.cpu); handle_native(name) } -pub fn tune_cpu(sess: &Session) -> Option<&str> { - match sess.opts.debugging_opts.tune_cpu { - Some(ref s) => Some(handle_native(&**s)), - None => None, +pub fn handle_native_features(sess: &Session) -> Vec { + match sess.opts.cg.target_cpu { + Some(ref s) => { + if s != "native" { + return vec![]; + } + + let features_string = unsafe { + let ptr = llvm::LLVMGetHostCPUFeatures(); + let features_string = if !ptr.is_null() { + CStr::from_ptr(ptr) + .to_str() + .unwrap_or_else(|e| { + bug!("LLVM returned a non-utf8 features string: {}", e); + }) + .to_owned() + } else { + bug!("could not allocate host CPU features, LLVM returned a `null` string"); + }; + + llvm::LLVMDisposeMessage(ptr); + + features_string + }; + + features_string.split(",").map(|s| s.to_owned()).collect() + } + None => vec![], } } + +pub fn tune_cpu(sess: &Session) -> Option<&str> { + let name = sess.opts.debugging_opts.tune_cpu.as_ref()?; + Some(handle_native(name)) +} diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index a43724fd49..8fd0caae47 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -7,13 +7,12 @@ use crate::llvm; use crate::llvm::{Bool, False, True}; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use rustc_ast as ast; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; use rustc_middle::bug; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_target::abi::call::{CastTarget, FnAbi, Reg}; use rustc_target::abi::{AddressSpace, Align, Integer, Size}; @@ -80,32 +79,32 @@ impl CodegenCx<'ll, 'tcx> { self.type_i8() } - crate fn type_int_from_ty(&self, t: ast::IntTy) -> &'ll Type { + crate fn type_int_from_ty(&self, t: ty::IntTy) -> &'ll Type { match t { - ast::IntTy::Isize => self.type_isize(), - ast::IntTy::I8 => self.type_i8(), - ast::IntTy::I16 => self.type_i16(), - ast::IntTy::I32 => self.type_i32(), - ast::IntTy::I64 => self.type_i64(), - ast::IntTy::I128 => self.type_i128(), + ty::IntTy::Isize => self.type_isize(), + ty::IntTy::I8 => self.type_i8(), + ty::IntTy::I16 => self.type_i16(), + ty::IntTy::I32 => self.type_i32(), + ty::IntTy::I64 => self.type_i64(), + ty::IntTy::I128 => self.type_i128(), } } - crate fn type_uint_from_ty(&self, t: ast::UintTy) -> &'ll Type { + crate fn type_uint_from_ty(&self, t: ty::UintTy) -> &'ll Type { match t { - ast::UintTy::Usize => self.type_isize(), - ast::UintTy::U8 => self.type_i8(), - ast::UintTy::U16 => self.type_i16(), - ast::UintTy::U32 => self.type_i32(), - ast::UintTy::U64 => self.type_i64(), - ast::UintTy::U128 => self.type_i128(), + ty::UintTy::Usize => self.type_isize(), + ty::UintTy::U8 => self.type_i8(), + ty::UintTy::U16 => self.type_i16(), + ty::UintTy::U32 => self.type_i32(), + ty::UintTy::U64 => self.type_i64(), + ty::UintTy::U128 => self.type_i128(), } } - crate fn type_float_from_ty(&self, t: ast::FloatTy) -> &'ll Type { + crate fn type_float_from_ty(&self, t: ty::FloatTy) -> &'ll Type { match t { - ast::FloatTy::F32 => self.type_f32(), - ast::FloatTy::F64 => self.type_f64(), + ty::FloatTy::F32 => self.type_f32(), + ty::FloatTy::F64 => self.type_f64(), } } diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 3fc56eecdd..39d08fbee3 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -9,7 +9,7 @@ use rustc_codegen_ssa::{ }; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::Ty; -use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size}; +use rustc_target::abi::{Align, Endian, HasDataLayout, LayoutOf, Size}; fn round_pointer_up_to_alignment( bx: &mut Builder<'a, 'll, 'tcx>, @@ -52,7 +52,7 @@ fn emit_direct_ptr_va_arg( let next = bx.inbounds_gep(addr, &[full_direct_size]); bx.store(next, va_list_addr, bx.tcx().data_layout.pointer_align.abi); - if size.bytes() < slot_size.bytes() && &*bx.tcx().sess.target.endian == "big" { + if size.bytes() < slot_size.bytes() && bx.tcx().sess.target.endian == Endian::Big { let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32); let adjusted = bx.inbounds_gep(addr, &[adjusted_size]); (bx.bitcast(adjusted, bx.cx().type_ptr_to(llty)), addr_align) @@ -105,7 +105,6 @@ fn emit_aapcs_va_arg( let mut end = bx.build_sibling_block("va_arg.end"); let zero = bx.const_i32(0); let offset_align = Align::from_bytes(4).unwrap(); - assert!(&*bx.tcx().sess.target.endian == "little"); let gr_type = target_ty.is_any_ptr() || target_ty.is_integral(); let (reg_off, reg_top_index, slot_size) = if gr_type { @@ -144,9 +143,14 @@ fn emit_aapcs_va_arg( let top = in_reg.load(top, bx.tcx().data_layout.pointer_align.abi); // reg_value = *(@top + reg_off_v); - let top = in_reg.gep(top, &[reg_off_v]); - let top = in_reg.bitcast(top, bx.cx.type_ptr_to(layout.llvm_type(bx))); - let reg_value = in_reg.load(top, layout.align.abi); + let mut reg_addr = in_reg.gep(top, &[reg_off_v]); + if bx.tcx().sess.target.endian == Endian::Big && layout.size.bytes() != slot_size { + // On big-endian systems the value is right-aligned in its slot. + let offset = bx.const_i32((slot_size - layout.size.bytes()) as i32); + reg_addr = in_reg.gep(reg_addr, &[offset]); + } + let reg_addr = in_reg.bitcast(reg_addr, bx.cx.type_ptr_to(layout.llvm_type(bx))); + let reg_value = in_reg.load(reg_addr, layout.align.abi); in_reg.br(&end.llbb()); // On Stack block diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index e5df0f6094..835f906239 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -10,6 +10,7 @@ test = false [dependencies] bitflags = "1.2.1" cc = "1.0.1" +itertools = "0.9" num_cpus = "1.0" memmap = "0.7" tracing = "0.1" diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index a3a2ef0417..8bc4e64422 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -14,7 +14,7 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; -use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor}; +use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo}; use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, Target}; use super::archive::ArchiveBuilder; @@ -74,7 +74,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( } }); - if outputs.outputs.should_codegen() { + if outputs.outputs.should_link() { let tmpdir = TempFileBuilder::new() .prefix("rustc") .tempdir() @@ -99,9 +99,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( path.as_ref(), target_cpu, ); - if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Split { - link_dwarf_object(sess, &out_filename); - } } } if sess.opts.json_artifact_notifications { @@ -123,9 +120,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( } }; - if sess.opts.output_types.should_codegen() - && !preserve_objects_for_their_debuginfo(sess) - { + if sess.opts.output_types.should_link() && !preserve_objects_for_their_debuginfo(sess) { for module in &codegen_results.modules { remove_temps_from_module(module); } @@ -166,7 +161,7 @@ fn get_linker( _ => match flavor { LinkerFlavor::Lld(f) => Command::lld(linker, f), LinkerFlavor::Msvc if sess.opts.cg.linker.is_none() && sess.target.linker.is_none() => { - Command::new(msvc_tool.as_ref().map(|t| t.path()).unwrap_or(linker)) + Command::new(msvc_tool.as_ref().map_or(linker, |t| t.path())) } _ => Command::new(linker), }, @@ -830,29 +825,43 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( } } - // On macOS, debuggers need this utility to get run to do some munging of - // the symbols. Note, though, that if the object files are being preserved - // for their debug information there's no need for us to run dsymutil. - if sess.target.is_like_osx - && sess.opts.debuginfo != DebugInfo::None - && !preserve_objects_for_their_debuginfo(sess) - { - let prog = Command::new("dsymutil").arg(out_filename).output(); - match prog { - Ok(prog) => { - if !prog.status.success() { - let mut output = prog.stderr.clone(); - output.extend_from_slice(&prog.stdout); - sess.struct_warn(&format!( - "processing debug info with `dsymutil` failed: {}", - prog.status - )) - .note(&escape_string(&output)) - .emit(); + match sess.split_debuginfo() { + // If split debug information is disabled or located in individual files + // there's nothing to do here. + SplitDebuginfo::Off | SplitDebuginfo::Unpacked => {} + + // If packed split-debuginfo is requested, but the final compilation + // doesn't actually have any debug information, then we skip this step. + SplitDebuginfo::Packed if sess.opts.debuginfo == DebugInfo::None => {} + + // On macOS the external `dsymutil` tool is used to create the packed + // debug information. Note that this will read debug information from + // the objects on the filesystem which we'll clean up later. + SplitDebuginfo::Packed if sess.target.is_like_osx => { + let prog = Command::new("dsymutil").arg(out_filename).output(); + match prog { + Ok(prog) => { + if !prog.status.success() { + let mut output = prog.stderr.clone(); + output.extend_from_slice(&prog.stdout); + sess.struct_warn(&format!( + "processing debug info with `dsymutil` failed: {}", + prog.status + )) + .note(&escape_string(&output)) + .emit(); + } } + Err(e) => sess.fatal(&format!("unable to run `dsymutil`: {}", e)), } - Err(e) => sess.fatal(&format!("unable to run `dsymutil`: {}", e)), } + + // On MSVC packed debug information is produced by the linker itself so + // there's no need to do anything else here. + SplitDebuginfo::Packed if sess.target.is_like_msvc => {} + + // ... and otherwise we're processing a `*.dwp` packed dwarf file. + SplitDebuginfo::Packed => link_dwarf_object(sess, &out_filename), } } @@ -887,23 +896,37 @@ fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linke } fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { - let default_sysroot = filesearch::get_or_default_sysroot(); - let default_tlib = - filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.triple()); + fn find_sanitizer_runtime(sess: &Session, filename: &String) -> PathBuf { + let session_tlib = + filesearch::make_target_lib_path(&sess.sysroot, sess.opts.target_triple.triple()); + let path = session_tlib.join(&filename); + if path.exists() { + return session_tlib; + } else { + let default_sysroot = filesearch::get_or_default_sysroot(); + let default_tlib = filesearch::make_target_lib_path( + &default_sysroot, + sess.opts.target_triple.triple(), + ); + return default_tlib; + } + } + let channel = option_env!("CFG_RELEASE_CHANNEL") .map(|channel| format!("-{}", channel)) .unwrap_or_default(); match sess.opts.target_triple.triple() { - "x86_64-apple-darwin" => { + "aarch64-apple-darwin" | "x86_64-apple-darwin" => { // On Apple platforms, the sanitizer is always built as a dylib, and // LLVM will link to `@rpath/*.dylib`, so we need to specify an // rpath to the library as well (the rpath should be absolute, see // PR #41352 for details). - let libname = format!("rustc{}_rt.{}", channel, name); - let rpath = default_tlib.to_str().expect("non-utf8 component in path"); + let filename = format!("rustc{}_rt.{}", channel, name); + let path = find_sanitizer_runtime(&sess, &filename); + let rpath = path.to_str().expect("non-utf8 component in path"); linker.args(&["-Wl,-rpath", "-Xlinker", rpath]); - linker.link_dylib(Symbol::intern(&libname)); + linker.link_dylib(Symbol::intern(&filename)); } "aarch64-fuchsia" | "aarch64-unknown-linux-gnu" @@ -911,7 +934,7 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { | "x86_64-unknown-freebsd" | "x86_64-unknown-linux-gnu" => { let filename = format!("librustc{}_rt.{}.a", channel, name); - let path = default_tlib.join(&filename); + let path = find_sanitizer_runtime(&sess, &filename).join(&filename); linker.link_whole_rlib(&path); } _ => {} @@ -1038,28 +1061,9 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { return false; } - // Single mode keeps debuginfo in the same object file, but in such a way that it it skipped - // by the linker - so it's expected that when codegen units are linked together that this - // debuginfo would be lost without keeping around the temps. - if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Single { - return true; - } - - // If we're on OSX then the equivalent of split dwarf is turned on by - // default. The final executable won't actually have any debug information - // except it'll have pointers to elsewhere. Historically we've always run - // `dsymutil` to "link all the dwarf together" but this is actually sort of - // a bummer for incremental compilation! (the whole point of split dwarf is - // that you don't do this sort of dwarf link). - // - // Basically as a result this just means that if we're on OSX and we're - // *not* running dsymutil then the object files are the only source of truth - // for debug information, so we must preserve them. - if sess.target.is_like_osx { - return !sess.opts.debugging_opts.run_dsymutil; - } - - false + // "unpacked" split debuginfo means that we leave object files as the + // debuginfo is found in the original object files themselves + sess.split_debuginfo() == SplitDebuginfo::Unpacked } pub fn archive_search_paths(sess: &Session) -> Vec { @@ -1276,6 +1280,7 @@ fn exec_linker( fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind { let kind = match (crate_type, sess.crt_static(Some(crate_type)), sess.relocation_model()) { + (CrateType::Executable, _, _) if sess.is_wasi_reactor() => LinkOutputKind::WasiReactorExe, (CrateType::Executable, false, RelocModel::Pic) => LinkOutputKind::DynamicPicExe, (CrateType::Executable, false, _) => LinkOutputKind::DynamicNoPicExe, (CrateType::Executable, true, RelocModel::Pic) => LinkOutputKind::StaticPicExe, @@ -2198,8 +2203,13 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { return; } }; - let arch_name = llvm_target.split('-').next().expect("LLVM target must have a hyphen"); - cmd.args(&["-arch", arch_name, "-isysroot", &sdk_root, "-Wl,-syslibroot", &sdk_root]); + if llvm_target.contains("macabi") { + cmd.args(&["-target", llvm_target]) + } else { + let arch_name = llvm_target.split('-').next().expect("LLVM target must have a hyphen"); + cmd.args(&["-arch", arch_name]) + } + cmd.args(&["-isysroot", &sdk_root, "-Wl,-syslibroot", &sdk_root]); } fn get_apple_sdk_root(sdk_name: &str) -> Result { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 3df956c465..bb35e7ec89 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -314,6 +314,10 @@ impl<'a> Linker for GccLinker<'a> { self.cmd.arg("-static"); self.build_dylib(out_filename); } + LinkOutputKind::WasiReactorExe => { + self.linker_arg("--entry"); + self.linker_arg("_initialize"); + } } // VxWorks compiler driver introduced `--static-crt` flag specifically for rustc, // it switches linking for libc and similar system libraries to static without using @@ -662,6 +666,9 @@ impl<'a> Linker for MsvcLinker<'a> { arg.push(out_filename.with_extension("dll.lib")); self.cmd.arg(arg); } + LinkOutputKind::WasiReactorExe => { + panic!("can't link as reactor on non-wasi target"); + } } } @@ -1085,6 +1092,10 @@ impl<'a> Linker for WasmLd<'a> { LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { self.cmd.arg("--no-entry"); } + LinkOutputKind::WasiReactorExe => { + self.cmd.arg("--entry"); + self.cmd.arg("_initialize"); + } } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index c84b87964b..6aef5cb535 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -282,6 +282,20 @@ pub struct TargetMachineFactoryConfig { pub split_dwarf_file: Option, } +impl TargetMachineFactoryConfig { + pub fn new( + cgcx: &CodegenContext, + module_name: &str, + ) -> TargetMachineFactoryConfig { + let split_dwarf_file = if cgcx.target_can_use_split_dwarf { + cgcx.output_filenames.split_dwarf_filename(cgcx.split_debuginfo, Some(module_name)) + } else { + None + }; + TargetMachineFactoryConfig { split_dwarf_file } + } +} + pub type TargetMachineFactoryFn = Arc< dyn Fn(TargetMachineFactoryConfig) -> Result<::TargetMachine, String> + Send @@ -311,10 +325,11 @@ pub struct CodegenContext { pub tm_factory: TargetMachineFactoryFn, pub msvc_imps_needed: bool, pub is_pe_coff: bool, + pub target_can_use_split_dwarf: bool, pub target_pointer_width: u32, pub target_arch: String, pub debuginfo: config::DebugInfo, - pub split_dwarf_kind: config::SplitDwarfKind, + pub split_debuginfo: rustc_target::spec::SplitDebuginfo, // Number of cgus excluding the allocator/metadata modules pub total_cgus: usize, @@ -1035,10 +1050,11 @@ fn start_executing_work( total_cgus, msvc_imps_needed: msvc_imps_needed(tcx), is_pe_coff: tcx.sess.target.is_like_windows, + target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(), target_pointer_width: tcx.sess.target.pointer_width, target_arch: tcx.sess.target.arch.clone(), debuginfo: tcx.sess.opts.debuginfo, - split_dwarf_kind: tcx.sess.opts.debugging_opts.split_dwarf, + split_debuginfo: tcx.sess.split_debuginfo(), }; // This is the "main loop" of parallel work happening for parallel codegen. diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 18132a2c7a..658ad3c375 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -1,18 +1,3 @@ -//! Codegen the completed AST to the LLVM IR. -//! -//! Some functions here, such as `codegen_block` and `codegen_expr`, return a value -- -//! the result of the codegen to LLVM -- while others, such as `codegen_fn` -//! and `mono_item`, are called only for the side effect of adding a -//! particular definition to the LLVM IR output we're producing. -//! -//! Hopefully useful general knowledge about codegen: -//! -//! * There's no way to find out the `Ty` type of a `Value`. Doing so -//! would be "trying to get the eggs out of an omelette" (credit: -//! pcwalton). You can, instead, find out its `llvm::Type` by calling `val_ty`, -//! but one `llvm::Type` corresponds to many `Ty`s; for instance, `tup(int, int, -//! int)` and `rec(x=int, y=int, z=int)` will have the same `llvm::Type`. - use crate::back::write::{ compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, ComputedLtoType, OngoingCodegen, @@ -27,8 +12,8 @@ use crate::{CachedModuleCodegen, CrateInfo, MemFlags, ModuleCodegen, ModuleKind} use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::profiling::print_time_passes_entry; -use rustc_data_structures::sync::{par_iter, Lock, ParallelIterator}; +use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; +use rustc_data_structures::sync::{par_iter, ParallelIterator}; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; @@ -44,15 +29,14 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::cgu_reuse_tracker::CguReuse; use rustc_session::config::{self, EntryFnType}; -use rustc_session::utils::NativeLibKind; use rustc_session::Session; -use rustc_symbol_mangling::test as symbol_names_test; use rustc_target::abi::{Align, LayoutOf, VariantIdx}; -use std::cmp; use std::ops::{Deref, DerefMut}; use std::time::{Duration, Instant}; +use itertools::Itertools; + pub fn bin_op_to_icmp_predicate(op: hir::BinOpKind, signed: bool) -> IntPredicate { match op { hir::BinOpKind::Eq => IntPredicate::IntEQ, @@ -486,8 +470,6 @@ pub fn codegen_crate( ongoing_codegen.codegen_finished(tcx); - finalize_tcx(tcx); - ongoing_codegen.check_for_errors(tcx.sess); return ongoing_codegen; @@ -565,16 +547,25 @@ pub fn codegen_crate( ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, metadata_module); } - // We sort the codegen units by size. This way we can schedule work for LLVM - // a bit more efficiently. - let codegen_units = { - let mut codegen_units = codegen_units.iter().collect::>(); - codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); - codegen_units + // For better throughput during parallel processing by LLVM, we used to sort + // CGUs largest to smallest. This would lead to better thread utilization + // by, for example, preventing a large CGU from being processed last and + // having only one LLVM thread working while the rest remained idle. + // + // However, this strategy would lead to high memory usage, as it meant the + // LLVM-IR for all of the largest CGUs would be resident in memory at once. + // + // Instead, we can compromise by ordering CGUs such that the largest and + // smallest are first, second largest and smallest are next, etc. If there + // are large size variations, this can reduce memory usage significantly. + let codegen_units: Vec<_> = { + let mut sorted_cgus = codegen_units.iter().collect::>(); + sorted_cgus.sort_by_cached_key(|cgu| cgu.size_estimate()); + + let (first_half, second_half) = sorted_cgus.split_at(sorted_cgus.len() / 2); + second_half.iter().rev().interleave(first_half).copied().collect() }; - let total_codegen_time = Lock::new(Duration::new(0, 0)); - // The non-parallel compiler can only translate codegen units to LLVM IR // on a single thread, leading to a staircase effect where the N LLVM // threads have to wait on the single codegen threads to generate work @@ -597,23 +588,26 @@ pub fn codegen_crate( .collect(); // Compile the found CGUs in parallel. - par_iter(cgus) + let start_time = Instant::now(); + + let pre_compiled_cgus = par_iter(cgus) .map(|(i, _)| { - let start_time = Instant::now(); let module = backend.compile_codegen_unit(tcx, codegen_units[i].name()); - let mut time = total_codegen_time.lock(); - *time += start_time.elapsed(); (i, module) }) - .collect() + .collect(); + + (pre_compiled_cgus, start_time.elapsed()) }) } else { - FxHashMap::default() + (FxHashMap::default(), Duration::new(0, 0)) } }; let mut cgu_reuse = Vec::new(); let mut pre_compiled_cgus: Option> = None; + let mut total_codegen_time = Duration::new(0, 0); + let start_rss = tcx.sess.time_passes().then(|| get_resident_set_size()); for (i, cgu) in codegen_units.iter().enumerate() { ongoing_codegen.wait_for_signal_to_codegen_item(); @@ -626,7 +620,9 @@ pub fn codegen_crate( codegen_units.iter().map(|cgu| determine_cgu_reuse(tcx, &cgu)).collect() }); // Pre compile some CGUs - pre_compiled_cgus = Some(pre_compile_cgus(&cgu_reuse)); + let (compiled_cgus, codegen_time) = pre_compile_cgus(&cgu_reuse); + pre_compiled_cgus = Some(compiled_cgus); + total_codegen_time += codegen_time; } let cgu_reuse = cgu_reuse[i]; @@ -640,10 +636,14 @@ pub fn codegen_crate( } else { let start_time = Instant::now(); let module = backend.compile_codegen_unit(tcx, cgu.name()); - let mut time = total_codegen_time.lock(); - *time += start_time.elapsed(); + total_codegen_time += start_time.elapsed(); module }; + // This will unwind if there are errors, which triggers our `AbortCodegenOnDrop` + // guard. Unfortunately, just skipping the `submit_codegened_module_to_llvm` makes + // compilation hang on post-monomorphization errors. + tcx.sess.abort_if_errors(); + submit_codegened_module_to_llvm( &backend, &ongoing_codegen.coordinator_send, @@ -682,20 +682,19 @@ pub fn codegen_crate( // Since the main thread is sometimes blocked during codegen, we keep track // -Ztime-passes output manually. - print_time_passes_entry( - tcx.sess.time_passes(), - "codegen_to_LLVM_IR", - total_codegen_time.into_inner(), - ); - - rustc_incremental::assert_module_sources::assert_module_sources(tcx); - - symbol_names_test::report_symbol_names(tcx); + if tcx.sess.time_passes() { + let end_rss = get_resident_set_size(); + + print_time_passes_entry( + "codegen_to_LLVM_IR", + total_codegen_time, + start_rss.unwrap(), + end_rss, + ); + } ongoing_codegen.check_for_errors(tcx.sess); - finalize_tcx(tcx); - ongoing_codegen.into_inner() } @@ -746,18 +745,6 @@ impl Drop for AbortCodegenOnDrop { } } -fn finalize_tcx(tcx: TyCtxt<'_>) { - tcx.sess.time("assert_dep_graph", || rustc_incremental::assert_dep_graph(tcx)); - tcx.sess.time("serialize_dep_graph", || rustc_incremental::save_dep_graph(tcx)); - - // We assume that no queries are run past here. If there are new queries - // after this point, they'll show up as "" in self-profiling data. - { - let _prof_timer = tcx.prof.generic_activity("self_profile_alloc_query_strings"); - tcx.alloc_self_profile_query_strings(); - } -} - impl CrateInfo { pub fn new(tcx: TyCtxt<'_>) -> CrateInfo { let mut info = CrateInfo { @@ -820,7 +807,7 @@ impl CrateInfo { } } -pub fn provide_both(providers: &mut Providers) { +pub fn provide(providers: &mut Providers) { providers.backend_optimization_level = |tcx, cratenum| { let for_speed = match tcx.sess.opts.optimize { // If globally no optimisation is done, #[optimize] has no effect. @@ -853,32 +840,6 @@ pub fn provide_both(providers: &mut Providers) { } tcx.sess.opts.optimize }; - - providers.dllimport_foreign_items = |tcx, krate| { - let module_map = tcx.foreign_modules(krate); - - let dllimports = tcx - .native_libraries(krate) - .iter() - .filter(|lib| { - if !matches!(lib.kind, NativeLibKind::Dylib | NativeLibKind::Unspecified) { - return false; - } - let cfg = match lib.cfg { - Some(ref cfg) => cfg, - None => return true, - }; - attr::cfg_matches(cfg, &tcx.sess.parse_sess, None) - }) - .filter_map(|lib| lib.foreign_module) - .map(|id| &module_map[&id]) - .flat_map(|module| module.foreign_items.iter().cloned()) - .collect(); - dllimports - }; - - providers.is_dllimport_foreign_item = - |tcx, def_id| tcx.dllimport_foreign_items(def_id.krate).contains(&def_id); } fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse { diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index b0d7953f51..549b8d41f5 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -170,30 +170,30 @@ impl<'tcx> FunctionCoverage<'tcx> { // `expression_index`s lower than the referencing `Expression`. Therefore, it is // reasonable to look up the new index of an expression operand while the `new_indexes` // vector is only complete up to the current `ExpressionIndex`. - let id_to_counter = - |new_indexes: &IndexVec>, - id: ExpressionOperandId| { - if id == ExpressionOperandId::ZERO { - Some(Counter::zero()) - } else if id.index() < self.counters.len() { - // Note: Some codegen-injected Counters may be only referenced by `Expression`s, - // and may not have their own `CodeRegion`s, - let index = CounterValueReference::from(id.index()); - Some(Counter::counter_value_reference(index)) - } else { - let index = self.expression_index(u32::from(id)); - self.expressions - .get(index) - .expect("expression id is out of range") - .as_ref() - // If an expression was optimized out, assume it would have produced a count - // of zero. This ensures that expressions dependent on optimized-out - // expressions are still valid. - .map_or(Some(Counter::zero()), |_| { - new_indexes[index].map(|new_index| Counter::expression(new_index)) - }) - } - }; + let id_to_counter = |new_indexes: &IndexVec< + InjectedExpressionIndex, + Option, + >, + id: ExpressionOperandId| { + if id == ExpressionOperandId::ZERO { + Some(Counter::zero()) + } else if id.index() < self.counters.len() { + // Note: Some codegen-injected Counters may be only referenced by `Expression`s, + // and may not have their own `CodeRegion`s, + let index = CounterValueReference::from(id.index()); + Some(Counter::counter_value_reference(index)) + } else { + let index = self.expression_index(u32::from(id)); + self.expressions + .get(index) + .expect("expression id is out of range") + .as_ref() + // If an expression was optimized out, assume it would have produced a count + // of zero. This ensures that expressions dependent on optimized-out + // expressions are still valid. + .map_or(Some(Counter::zero()), |_| new_indexes[index].map(Counter::expression)) + } + }; for (original_index, expression) in self.expressions.iter_enumerated().filter_map(|(original_index, entry)| { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index bc93bd8b7b..0307117e1c 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -116,7 +116,7 @@ pub struct NativeLib { impl From<&cstore::NativeLib> for NativeLib { fn from(lib: &cstore::NativeLib) -> Self { - NativeLib { kind: lib.kind.clone(), name: lib.name.clone(), cfg: lib.cfg.clone() } + NativeLib { kind: lib.kind, name: lib.name, cfg: lib.cfg.clone() } } } @@ -160,13 +160,12 @@ pub struct CodegenResults { pub fn provide(providers: &mut Providers) { crate::back::symbol_export::provide(providers); - crate::base::provide_both(providers); + crate::base::provide(providers); crate::target_features::provide(providers); } pub fn provide_extern(providers: &mut Providers) { crate::back::symbol_export::provide_extern(providers); - crate::base::provide_both(providers); } /// Checks if the given filename ends with the `.rcgu.o` extension that `rustc` diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 44bb0deeae..fd0ff5b66e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -104,7 +104,7 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { ) { let cx = self.fx.cx; - if let &[ref proj_base @ .., elem] = place_ref.projection { + if let Some((place_base, elem)) = place_ref.last_projection() { let mut base_context = if context.is_mutating_use() { PlaceContext::MutatingUse(MutatingUseContext::Projection) } else { @@ -112,15 +112,14 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { }; // Allow uses of projections that are ZSTs or from scalar fields. - let is_consume = match context { + let is_consume = matches!( + context, PlaceContext::NonMutatingUse( NonMutatingUseContext::Copy | NonMutatingUseContext::Move, - ) => true, - _ => false, - }; + ) + ); if is_consume { - let base_ty = - mir::Place::ty_from(place_ref.local, proj_base, self.fx.mir, cx.tcx()); + let base_ty = place_base.ty(self.fx.mir, cx.tcx()); let base_ty = self.fx.monomorphize(base_ty); // ZSTs don't require any actual memory access. @@ -175,11 +174,7 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { base_context = context; } - self.process_place( - &mir::PlaceRef { local: place_ref.local, projection: proj_base }, - base_context, - location, - ); + self.process_place(&place_base, base_context, location); // HACK(eddyb) this emulates the old `visit_projection_elem`, this // entire `visit_place`-like `process_place` method should be rewritten, // now that we have moved to the "slice of projections" representation. diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index ce56f16354..c821908167 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -522,7 +522,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mut bx: Bx, terminator: &mir::Terminator<'tcx>, func: &mir::Operand<'tcx>, - args: &Vec>, + args: &[mir::Operand<'tcx>], destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>, cleanup: Option, fn_span: Span, @@ -875,20 +875,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ty::Uint(_) => value.to_string(), ty::Int(int_ty) => { match int_ty.normalize(bx.tcx().sess.target.pointer_width) { - ast::IntTy::I8 => (value as i8).to_string(), - ast::IntTy::I16 => (value as i16).to_string(), - ast::IntTy::I32 => (value as i32).to_string(), - ast::IntTy::I64 => (value as i64).to_string(), - ast::IntTy::I128 => (value as i128).to_string(), - ast::IntTy::Isize => unreachable!(), + ty::IntTy::I8 => (value as i8).to_string(), + ty::IntTy::I16 => (value as i16).to_string(), + ty::IntTy::I32 => (value as i32).to_string(), + ty::IntTy::I64 => (value as i64).to_string(), + ty::IntTy::I128 => (value as i128).to_string(), + ty::IntTy::Isize => unreachable!(), } } - ty::Float(ast::FloatTy::F32) => { - f32::from_bits(value as u32).to_string() - } - ty::Float(ast::FloatTy::F64) => { - f64::from_bits(value as u64).to_string() - } + ty::Float(ty::FloatTy::F32) => f32::from_bits(value as u32).to_string(), + ty::Float(ty::FloatTy::F64) => f64::from_bits(value as u64).to_string(), _ => span_bug!(span, "asm const has bad type {}", ty), }; InlineAsmOperandRef::Const { string } diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 3a85c268e0..b79a221a0e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -30,12 +30,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { .tcx() .const_eval_resolve(ty::ParamEnv::reveal_all(), def, substs, promoted, None) .map_err(|err| { - if promoted.is_none() { - self.cx - .tcx() - .sess - .span_err(constant.span, "erroneous constant encountered"); - } + self.cx.tcx().sess.span_err(constant.span, "erroneous constant encountered"); err }), ty::ConstKind::Value(value) => Ok(value), diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index d5b2cbaa55..f1eae605da 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -170,7 +170,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // (after #67586 gets fixed). None } else { - let name = kw::Invalid; + let name = kw::Empty; let decl = &self.mir.local_decls[local]; let dbg_var = if full_debug_info { self.adjusted_span_and_dbg_scope(decl.source_info).map( @@ -204,7 +204,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None } else { Some(match whole_local_var.or(fallback_var) { - Some(var) if var.name != kw::Invalid => var.name.to_string(), + Some(var) if var.name != kw::Empty => var.name.to_string(), _ => format!("{:?}", local), }) }; diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 285140060b..d31ececf13 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -188,8 +188,11 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut bx); + // Evaluate all required consts; codegen later assumes that CTFE will never fail. + let mut all_consts_ok = true; for const_ in &mir.required_consts { if let Err(err) = fx.eval_mir_constant(const_) { + all_consts_ok = false; match err { // errored or at least linted ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {} @@ -199,6 +202,11 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } } + if !all_consts_ok { + // We leave the IR in some half-built state here, and rely on this code not even being + // submitted to LLVM once an error was raised. + return; + } let memory_locals = analyze::non_ssa_locals(&fx); diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 08a4ae3962..25e84c38ed 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -6,9 +6,8 @@ use crate::glue; use crate::traits::*; use crate::MemFlags; -use rustc_errors::ErrorReported; use rustc_middle::mir; -use rustc_middle::mir::interpret::{ConstValue, ErrorHandled, Pointer, Scalar}; +use rustc_middle::mir::interpret::{ConstValue, Pointer, Scalar}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::Ty; use rustc_target::abi::{Abi, Align, LayoutOf, Size}; @@ -439,25 +438,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::Operand::Constant(ref constant) => { - self.eval_mir_constant_to_operand(bx, constant).unwrap_or_else(|err| { - match err { - // errored or at least linted - ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {} - ErrorHandled::TooGeneric => { - bug!("codegen encountered polymorphic constant") - } - } - // Allow RalfJ to sleep soundly knowing that even refactorings that remove - // the above error (or silence it under some conditions) will not cause UB. - bx.abort(); - // We still have to return an operand but it doesn't matter, - // this code is unreachable. - let ty = self.monomorphize(constant.literal.ty); - let layout = bx.cx().layout_of(ty); - bx.load_operand(PlaceRef::new_sized( - bx.cx().const_undef(bx.cx().type_ptr_to(bx.cx().backend_type(layout))), - layout, - )) + // This cannot fail because we checked all required_consts in advance. + self.eval_mir_constant_to_operand(bx, constant).unwrap_or_else(|_err| { + span_bug!(constant.span, "erroneous constant not captured by required_consts") }) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index e4f4c88447..66d9d1a1e0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -178,16 +178,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // Get the alignment of the field let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta); - // Bump the unaligned offset up to the appropriate alignment using the - // following expression: - // - // (unaligned offset + (align - 1)) & -align - - // Calculate offset. - let align_sub_1 = bx.sub(unsized_align, bx.cx().const_usize(1u64)); - let and_lhs = bx.add(unaligned_offset, align_sub_1); - let and_rhs = bx.neg(unsized_align); - let offset = bx.and(and_lhs, and_rhs); + // Bump the unaligned offset up to the appropriate alignment + let offset = round_up_const_value_to_alignment(bx, unaligned_offset, unsized_align); debug!("struct_field_ptr: DST field offset: {:?}", offset); @@ -514,7 +506,49 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn monomorphized_place_ty(&self, place_ref: mir::PlaceRef<'tcx>) -> Ty<'tcx> { let tcx = self.cx.tcx(); - let place_ty = mir::Place::ty_from(place_ref.local, place_ref.projection, self.mir, tcx); + let place_ty = place_ref.ty(self.mir, tcx); self.monomorphize(place_ty.ty) } } + +fn round_up_const_value_to_alignment<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + value: Bx::Value, + align: Bx::Value, +) -> Bx::Value { + // In pseudo code: + // + // if value & (align - 1) == 0 { + // value + // } else { + // (value & !(align - 1)) + align + // } + // + // Usually this is written without branches as + // + // (value + align - 1) & !(align - 1) + // + // But this formula cannot take advantage of constant `value`. E.g. if `value` is known + // at compile time to be `1`, this expression should be optimized to `align`. However, + // optimization only holds if `align` is a power of two. Since the optimizer doesn't know + // that `align` is a power of two, it cannot perform this optimization. + // + // Instead we use + // + // value + (-value & (align - 1)) + // + // Since `align` is used only once, the expression can be optimized. For `value = 0` + // its optimized to `0` even in debug mode. + // + // NB: The previous version of this code used + // + // (value + align - 1) & -align + // + // Even though `-align == !(align - 1)`, LLVM failed to optimize this even for + // `value = 0`. Bug report: https://bugs.llvm.org/show_bug.cgi?id=48559 + let one = bx.const_usize(1); + let align_minus_1 = bx.sub(align, one); + let neg_value = bx.neg(value); + let offset = bx.and(neg_value, align_minus_1); + bx.add(value, offset) +} diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs index 01efcaf6f4..08c3419a84 100644 --- a/compiler/rustc_data_structures/src/fingerprint.rs +++ b/compiler/rustc_data_structures/src/fingerprint.rs @@ -1,10 +1,10 @@ use crate::stable_hasher; use rustc_serialize::{ - opaque::{self, EncodeResult}, + opaque::{self, EncodeResult, FileEncodeResult}, Decodable, Encodable, }; use std::hash::{Hash, Hasher}; -use std::mem; +use std::mem::{self, MaybeUninit}; #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)] pub struct Fingerprint(u64, u64); @@ -53,15 +53,8 @@ impl Fingerprint { format!("{:x}{:x}", self.0, self.1) } - pub fn encode_opaque(&self, encoder: &mut opaque::Encoder) -> EncodeResult { - let bytes: [u8; 16] = unsafe { mem::transmute([self.0.to_le(), self.1.to_le()]) }; - - encoder.emit_raw_bytes(&bytes); - Ok(()) - } - pub fn decode_opaque(decoder: &mut opaque::Decoder<'_>) -> Result { - let mut bytes = [0; 16]; + let mut bytes: [MaybeUninit; 16] = MaybeUninit::uninit_array(); decoder.read_raw_bytes(&mut bytes)?; @@ -142,7 +135,16 @@ impl FingerprintEncoder for E { impl FingerprintEncoder for opaque::Encoder { fn encode_fingerprint(&mut self, f: &Fingerprint) -> EncodeResult { - f.encode_opaque(self) + let bytes: [u8; 16] = unsafe { mem::transmute([f.0.to_le(), f.1.to_le()]) }; + self.emit_raw_bytes(&bytes); + Ok(()) + } +} + +impl FingerprintEncoder for opaque::FileEncoder { + fn encode_fingerprint(&mut self, f: &Fingerprint) -> FileEncodeResult { + let bytes: [u8; 16] = unsafe { mem::transmute([f.0.to_le(), f.1.to_le()]) }; + self.emit_raw_bytes(&bytes) } } @@ -198,7 +200,7 @@ impl Encodable for PackedFingerprint { impl Decodable for PackedFingerprint { #[inline] fn decode(d: &mut D) -> Result { - Fingerprint::decode(d).map(|f| PackedFingerprint(f)) + Fingerprint::decode(d).map(PackedFingerprint) } } diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 1cfbce2355..ad62e3c9fc 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -8,7 +8,6 @@ use super::iterate::reverse_post_order; use super::ControlFlowGraph; use rustc_index::vec::{Idx, IndexVec}; -use std::borrow::BorrowMut; use std::cmp::Ordering; #[cfg(test)] @@ -20,22 +19,17 @@ pub fn dominators(graph: G) -> Dominators { dominators_given_rpo(graph, &rpo) } -fn dominators_given_rpo>( - mut graph: G, - rpo: &[G::Node], -) -> Dominators { - let start_node = graph.borrow().start_node(); +fn dominators_given_rpo(graph: G, rpo: &[G::Node]) -> Dominators { + let start_node = graph.start_node(); assert_eq!(rpo[0], start_node); // compute the post order index (rank) for each node - let mut post_order_rank: IndexVec = - (0..graph.borrow().num_nodes()).map(|_| 0).collect(); + let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes()); for (index, node) in rpo.iter().rev().cloned().enumerate() { post_order_rank[node] = index; } - let mut immediate_dominators: IndexVec> = - (0..graph.borrow().num_nodes()).map(|_| None).collect(); + let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes()); immediate_dominators[start_node] = Some(start_node); let mut changed = true; @@ -44,7 +38,7 @@ fn dominators_given_rpo>( for &node in &rpo[1..] { let mut new_idom = None; - for pred in graph.borrow_mut().predecessors(node) { + for pred in graph.predecessors(node) { if immediate_dominators[pred].is_some() { // (*) dominators for `pred` have been calculated new_idom = Some(if let Some(new_idom) = new_idom { diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 5b3d8233f3..e2cbb09ce5 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -523,7 +523,7 @@ where successors_len: 0, min_depth: depth, min_cycle_root: successor_node, - successor_node: successor_node, + successor_node, }); continue 'recurse; } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index d903a557c7..5880bbd3de 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -15,8 +15,7 @@ #![feature(fn_traits)] #![feature(int_bits_const)] #![feature(min_specialization)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![feature(nll)] #![feature(allow_internal_unstable)] #![feature(hash_raw_entry)] @@ -27,7 +26,7 @@ #![feature(thread_id_value)] #![feature(extend_one)] #![feature(const_panic)] -#![feature(min_const_generics)] +#![cfg_attr(bootstrap, feature(min_const_generics))] #![feature(new_uninit)] #![feature(once_cell)] #![feature(maybe_uninit_uninit_array)] diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 5d13b7f27c..f0b413c795 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -166,7 +166,7 @@ impl SelfProfilerRef { // If there is no SelfProfiler then the filter mask is set to NONE, // ensuring that nothing ever tries to actually access it. let event_filter_mask = - profiler.as_ref().map(|p| p.event_filter_mask).unwrap_or(EventFilter::empty()); + profiler.as_ref().map_or(EventFilter::empty(), |p| p.event_filter_mask); SelfProfilerRef { profiler, @@ -555,13 +555,16 @@ impl<'a> TimingGuard<'a> { #[must_use] pub struct VerboseTimingGuard<'a> { - start_and_message: Option<(Instant, String)>, + start_and_message: Option<(Instant, Option, String)>, _guard: TimingGuard<'a>, } impl<'a> VerboseTimingGuard<'a> { pub fn start(message: Option, _guard: TimingGuard<'a>) -> Self { - VerboseTimingGuard { _guard, start_and_message: message.map(|msg| (Instant::now(), msg)) } + VerboseTimingGuard { + _guard, + start_and_message: message.map(|msg| (Instant::now(), get_resident_set_size(), msg)), + } } #[inline(always)] @@ -573,25 +576,39 @@ impl<'a> VerboseTimingGuard<'a> { impl Drop for VerboseTimingGuard<'_> { fn drop(&mut self) { - if let Some((start, ref message)) = self.start_and_message { - print_time_passes_entry(true, &message[..], start.elapsed()); + if let Some((start_time, start_rss, ref message)) = self.start_and_message { + let end_rss = get_resident_set_size(); + print_time_passes_entry(&message[..], start_time.elapsed(), start_rss, end_rss); } } } -pub fn print_time_passes_entry(do_it: bool, what: &str, dur: Duration) { - if !do_it { - return; - } - - let mem_string = match get_resident() { - Some(n) => { - let mb = n as f64 / 1_000_000.0; - format!("; rss: {}MB", mb.round() as usize) +pub fn print_time_passes_entry( + what: &str, + dur: Duration, + start_rss: Option, + end_rss: Option, +) { + let rss_to_mb = |rss| (rss as f64 / 1_000_000.0).round() as usize; + let rss_change_to_mb = |rss| (rss as f64 / 1_000_000.0).round() as i128; + + let mem_string = match (start_rss, end_rss) { + (Some(start_rss), Some(end_rss)) => { + let change_rss = end_rss as i128 - start_rss as i128; + + format!( + "; rss: {:>4}MB -> {:>4}MB ({:>+5}MB)", + rss_to_mb(start_rss), + rss_to_mb(end_rss), + rss_change_to_mb(change_rss), + ) } - None => String::new(), + (Some(start_rss), None) => format!("; rss start: {:>4}MB", rss_to_mb(start_rss)), + (None, Some(end_rss)) => format!("; rss end: {:>4}MB", rss_to_mb(end_rss)), + (None, None) => String::new(), }; - println!("time: {}{}\t{}", duration_to_secs_str(dur), mem_string, what); + + println!("time: {:>7}{}\t{}", duration_to_secs_str(dur), mem_string, what); } // Hack up our own formatting for the duration to make it easier for scripts @@ -603,7 +620,7 @@ pub fn duration_to_secs_str(dur: std::time::Duration) -> String { // Memory reporting cfg_if! { if #[cfg(windows)] { - fn get_resident() -> Option { + pub fn get_resident_set_size() -> Option { use std::mem::{self, MaybeUninit}; use winapi::shared::minwindef::DWORD; use winapi::um::processthreadsapi::GetCurrentProcess; @@ -621,7 +638,7 @@ cfg_if! { } } } else if #[cfg(unix)] { - fn get_resident() -> Option { + pub fn get_resident_set_size() -> Option { let field = 1; let contents = fs::read("/proc/self/statm").ok()?; let contents = String::from_utf8(contents).ok()?; @@ -630,7 +647,7 @@ cfg_if! { Some(npages * 4096) } } else { - fn get_resident() -> Option { + pub fn get_resident_set_size() -> Option { None } } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 579eb1cb7d..3850c9b74f 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -552,6 +552,7 @@ pub fn hash_stable_hashmap( /// A vector container that makes sure that its items are hashed in a stable /// order. +#[derive(Debug)] pub struct StableVec(Vec); impl StableVec { diff --git a/compiler/rustc_data_structures/src/steal.rs b/compiler/rustc_data_structures/src/steal.rs index e532a84cea..30f659c2f7 100644 --- a/compiler/rustc_data_structures/src/steal.rs +++ b/compiler/rustc_data_structures/src/steal.rs @@ -21,6 +21,7 @@ use crate::sync::{MappedReadGuard, ReadGuard, RwLock}; /// -- once the value is stolen -- it will never be read from again. // // FIXME(#41710): what is the best way to model linear queries? +#[derive(Debug)] pub struct Steal { value: RwLock>, } @@ -30,6 +31,7 @@ impl Steal { Steal { value: RwLock::new(Some(value)) } } + #[track_caller] pub fn borrow(&self) -> MappedReadGuard<'_, T> { ReadGuard::map(self.value.borrow(), |opt| match *opt { None => panic!("attempted to read from stolen value"), @@ -37,10 +39,11 @@ impl Steal { }) } + #[track_caller] pub fn steal(&self) -> T { let value_ref = &mut *self.value.try_write().expect("stealing value which is locked"); let value = value_ref.take(); - value.expect("attempt to read from stolen value") + value.expect("attempt to steal from stolen value") } } diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 0adc006b62..b88b556d14 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["dylib"] [dependencies] libc = "0.2" +atty = "0.2" tracing = { version = "0.1.18" } tracing-subscriber = { version = "0.2.13", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } tracing-tree = "0.1.6" diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index b3466f49b9..8295e88f75 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -16,7 +16,7 @@ pub extern crate rustc_plugin_impl as plugin; use rustc_ast as ast; use rustc_codegen_ssa::{traits::CodegenBackend, CodegenResults}; -use rustc_data_structures::profiling::print_time_passes_entry; +use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::SeqCst; use rustc_errors::registry::{InvalidErrorCode, Registry}; use rustc_errors::{ErrorReported, PResult}; @@ -546,24 +546,12 @@ impl Compilation { #[derive(Copy, Clone)] pub struct RustcDefaultCalls; -// FIXME remove these and use winapi 0.3 instead -// Duplicates: bootstrap/compile.rs, librustc_errors/emitter.rs -#[cfg(unix)] fn stdout_isatty() -> bool { - unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 } + atty::is(atty::Stream::Stdout) } -#[cfg(windows)] -fn stdout_isatty() -> bool { - use winapi::um::consoleapi::GetConsoleMode; - use winapi::um::processenv::GetStdHandle; - use winapi::um::winbase::STD_OUTPUT_HANDLE; - - unsafe { - let handle = GetStdHandle(STD_OUTPUT_HANDLE); - let mut out = 0; - GetConsoleMode(handle, &mut out) != 0 - } +fn stderr_isatty() -> bool { + atty::is(atty::Stream::Stderr) } fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { @@ -603,7 +591,7 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { } } -fn show_content_with_pager(content: &String) { +fn show_content_with_pager(content: &str) { let pager_name = env::var_os("PAGER").unwrap_or_else(|| { if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") } }); @@ -810,7 +798,7 @@ pub fn version(binary: &str, matches: &getopts::Matches) { println!("commit-date: {}", unw(util::commit_date_str())); println!("host: {}", config::host_triple()); println!("release: {}", unw(util::release_str())); - if cfg!(llvm) { + if cfg!(feature = "llvm") { get_builtin_codegen_backend("llvm")().print_version(); } } @@ -1099,7 +1087,7 @@ pub fn handle_options(args: &[String]) -> Option { } if cg_flags.iter().any(|x| *x == "passes=list") { - if cfg!(llvm) { + if cfg!(feature = "llvm") { get_builtin_codegen_backend("llvm")().print_passes(); } return None; @@ -1248,7 +1236,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { } // If backtraces are enabled, also print the query stack - let backtrace = env::var_os("RUST_BACKTRACE").map(|x| &x != "0").unwrap_or(false); + let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0"); let num_frames = if backtrace { None } else { Some(2) }; @@ -1290,7 +1278,7 @@ pub fn init_env_logger(env: &str) { Ok(value) => match value.as_ref() { "always" => true, "never" => false, - "auto" => stdout_isatty(), + "auto" => stderr_isatty(), _ => early_error( ErrorOutputType::default(), &format!( @@ -1299,7 +1287,7 @@ pub fn init_env_logger(env: &str) { ), ), }, - Err(std::env::VarError::NotPresent) => stdout_isatty(), + Err(std::env::VarError::NotPresent) => stderr_isatty(), Err(std::env::VarError::NotUnicode(_value)) => early_error( ErrorOutputType::default(), "non-Unicode log color value: expected one of always, never, or auto", @@ -1324,7 +1312,8 @@ pub fn init_env_logger(env: &str) { } pub fn main() -> ! { - let start = Instant::now(); + let start_time = Instant::now(); + let start_rss = get_resident_set_size(); init_rustc_env_logger(); let mut callbacks = TimePassesCallbacks::default(); install_ice_hook(); @@ -1342,7 +1331,11 @@ pub fn main() -> ! { .collect::>(); RunCompiler::new(&args, &mut callbacks).run() }); - // The extra `\t` is necessary to align this label with the others. - print_time_passes_entry(callbacks.time_passes, "\ttotal", start.elapsed()); + + if callbacks.time_passes { + let end_rss = get_resident_set_size(); + print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss); + } + process::exit(exit_code) } diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs index 305fa838af..b7edc24bc4 100644 --- a/compiler/rustc_driver/src/pretty.rs +++ b/compiler/rustc_driver/src/pretty.rs @@ -363,8 +363,15 @@ impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> { fn get_source(input: &Input, sess: &Session) -> (String, FileName) { let src_name = input.source_name(); - let src = - String::clone(&sess.source_map().get_source_file(&src_name).unwrap().src.as_ref().unwrap()); + let src = String::clone( + &sess + .source_map() + .get_source_file(&src_name) + .expect("get_source_file") + .src + .as_ref() + .expect("src"), + ); (src, src_name) } diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index fef6602b9c..1ed43669ad 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -267,6 +267,7 @@ E0516: include_str!("./error_codes/E0516.md"), E0517: include_str!("./error_codes/E0517.md"), E0518: include_str!("./error_codes/E0518.md"), E0520: include_str!("./error_codes/E0520.md"), +E0521: include_str!("./error_codes/E0521.md"), E0522: include_str!("./error_codes/E0522.md"), E0524: include_str!("./error_codes/E0524.md"), E0525: include_str!("./error_codes/E0525.md"), @@ -464,6 +465,8 @@ E0776: include_str!("./error_codes/E0776.md"), E0777: include_str!("./error_codes/E0777.md"), E0778: include_str!("./error_codes/E0778.md"), E0779: include_str!("./error_codes/E0779.md"), +E0780: include_str!("./error_codes/E0780.md"), +E0781: include_str!("./error_codes/E0781.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard @@ -596,7 +599,6 @@ E0779: include_str!("./error_codes/E0779.md"), E0514, // metadata version mismatch E0519, // local crate and dependency have same (crate-name, disambiguator) // two dependencies have same (crate-name, disambiguator) but different SVH - E0521, // borrowed data escapes outside of closure E0523, // E0526, // shuffle indices are not constant // E0540, // multiple rustc_deprecated attributes diff --git a/compiler/rustc_error_codes/src/error_codes/E0013.md b/compiler/rustc_error_codes/src/error_codes/E0013.md index 8de177590e..5605302772 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0013.md +++ b/compiler/rustc_error_codes/src/error_codes/E0013.md @@ -8,7 +8,7 @@ static X: i32 = 42; const Y: i32 = X; ``` -In this example, `Y` cannot refer to `X` here. To fix this, the value can be +In this example, `Y` cannot refer to `X`. To fix this, the value can be extracted as a const and then used: ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0038.md b/compiler/rustc_error_codes/src/error_codes/E0038.md index b2cc2a2273..019d54b620 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0038.md +++ b/compiler/rustc_error_codes/src/error_codes/E0038.md @@ -287,5 +287,5 @@ the method `get_a()` would return an object of unknown type when called on the function. `Self` type parameters let us make object safe traits no longer safe, so they are forbidden when specifying supertraits. -There's no easy fix for this, generally code will need to be refactored so that +There's no easy fix for this. Generally, code will need to be refactored so that you no longer need to derive from `Super`. diff --git a/compiler/rustc_error_codes/src/error_codes/E0044.md b/compiler/rustc_error_codes/src/error_codes/E0044.md index 635ff95329..ed7daf8ddd 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0044.md +++ b/compiler/rustc_error_codes/src/error_codes/E0044.md @@ -3,13 +3,13 @@ You cannot use type or const parameters on foreign items. Example of erroneous code: ```compile_fail,E0044 -extern { fn some_func(x: T); } +extern "C" { fn some_func(x: T); } ``` To fix this, replace the generic parameter with the specializations that you need: ``` -extern { fn some_func_i32(x: i32); } -extern { fn some_func_i64(x: i64); } +extern "C" { fn some_func_i32(x: i32); } +extern "C" { fn some_func_i64(x: i64); } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0107.md b/compiler/rustc_error_codes/src/error_codes/E0107.md index 4d22b17fe1..4e37695a52 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0107.md +++ b/compiler/rustc_error_codes/src/error_codes/E0107.md @@ -1,4 +1,4 @@ -An incorrect number of generic arguments were provided. +An incorrect number of generic arguments was provided. Erroneous code example: diff --git a/compiler/rustc_error_codes/src/error_codes/E0116.md b/compiler/rustc_error_codes/src/error_codes/E0116.md index ca849c2a12..653be60298 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0116.md +++ b/compiler/rustc_error_codes/src/error_codes/E0116.md @@ -10,7 +10,7 @@ You can only define an inherent implementation for a type in the same crate where the type was defined. For example, an `impl` block as above is not allowed since `Vec` is defined in the standard library. -To fix this problem, you can do either of these things: +To fix this problem, you can either: - define a trait that has the desired associated functions/types/constants and implement the trait for the type in question diff --git a/compiler/rustc_error_codes/src/error_codes/E0130.md b/compiler/rustc_error_codes/src/error_codes/E0130.md index a270feaf58..2cd27b5ec0 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0130.md +++ b/compiler/rustc_error_codes/src/error_codes/E0130.md @@ -3,7 +3,7 @@ A pattern was declared as an argument in a foreign function declaration. Erroneous code example: ```compile_fail,E0130 -extern { +extern "C" { fn foo((a, b): (u32, u32)); // error: patterns aren't allowed in foreign // function declarations } @@ -17,7 +17,7 @@ struct SomeStruct { b: u32, } -extern { +extern "C" { fn foo(s: SomeStruct); // ok! } ``` @@ -25,7 +25,7 @@ extern { Or: ``` -extern { +extern "C" { fn foo(a: (u32, u32)); // ok! } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0207.md b/compiler/rustc_error_codes/src/error_codes/E0207.md index cb4f5d5157..8a7923ac93 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0207.md +++ b/compiler/rustc_error_codes/src/error_codes/E0207.md @@ -14,7 +14,7 @@ impl Foo { } ``` -Any type parameter parameter of an `impl` must meet at least one of +Any type parameter of an `impl` must meet at least one of the following criteria: - it appears in the _implementing type_ of the impl, e.g. `impl Foo` diff --git a/compiler/rustc_error_codes/src/error_codes/E0277.md b/compiler/rustc_error_codes/src/error_codes/E0277.md index 2e2cd5e01f..9f6db6ed7a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0277.md +++ b/compiler/rustc_error_codes/src/error_codes/E0277.md @@ -59,9 +59,9 @@ fn main() { } ``` -Note that the error here is in the definition of the generic function: Although +Note that the error here is in the definition of the generic function. Although we only call it with a parameter that does implement `Debug`, the compiler -still rejects the function: It must work with all possible input types. In +still rejects the function. It must work with all possible input types. In order to make this example compile, we need to restrict the generic type we're accepting: diff --git a/compiler/rustc_error_codes/src/error_codes/E0309.md b/compiler/rustc_error_codes/src/error_codes/E0309.md index e719ee590a..c36a56b00c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0309.md +++ b/compiler/rustc_error_codes/src/error_codes/E0309.md @@ -25,7 +25,7 @@ where The type definition contains some field whose type requires an outlives annotation. Outlives annotations (e.g., `T: 'a`) are used to guarantee that all -the data in T is valid for at least the lifetime `'a`. This scenario most +the data in `T` is valid for at least the lifetime `'a`. This scenario most commonly arises when the type contains an associated type reference like `>::Output`, as shown in the previous code. diff --git a/compiler/rustc_error_codes/src/error_codes/E0373.md b/compiler/rustc_error_codes/src/error_codes/E0373.md index fd96987793..effa597aad 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0373.md +++ b/compiler/rustc_error_codes/src/error_codes/E0373.md @@ -50,3 +50,24 @@ fn foo() -> Box u32> { Now that the closure has its own copy of the data, there's no need to worry about safety. + +This error may also be encountered while using `async` blocks: + +```compile_fail,E0373,edition2018 +use std::future::Future; + +async fn f() { + let v = vec![1, 2, 3i32]; + spawn(async { //~ ERROR E0373 + println!("{:?}", v) + }); +} + +fn spawn(future: F) { + unimplemented!() +} +``` + +Similarly to closures, `async` blocks are not executed immediately and may +capture closed-over data by reference. For more information, see +https://rust-lang.github.io/async-book/03_async_await/01_chapter.html. diff --git a/compiler/rustc_error_codes/src/error_codes/E0435.md b/compiler/rustc_error_codes/src/error_codes/E0435.md index 424e5ce1e2..798a20d48b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0435.md +++ b/compiler/rustc_error_codes/src/error_codes/E0435.md @@ -7,6 +7,12 @@ let foo = 42; let a: [u8; foo]; // error: attempt to use a non-constant value in a constant ``` +'constant' means 'a compile-time value'. + +More details can be found in the [Variables and Mutability] section of the book. + +[Variables and Mutability]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants + To fix this error, please replace the value with a constant. Example: ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0454.md b/compiler/rustc_error_codes/src/error_codes/E0454.md index 23ca6c7824..95a22b92e3 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0454.md +++ b/compiler/rustc_error_codes/src/error_codes/E0454.md @@ -3,7 +3,7 @@ A link name was given with an empty name. Erroneous code example: ```compile_fail,E0454 -#[link(name = "")] extern {} +#[link(name = "")] extern "C" {} // error: `#[link(name = "")]` given with empty name ``` @@ -11,5 +11,5 @@ The rust compiler cannot link to an external library if you don't give it its name. Example: ```no_run -#[link(name = "some_lib")] extern {} // ok! +#[link(name = "some_lib")] extern "C" {} // ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0455.md b/compiler/rustc_error_codes/src/error_codes/E0455.md index 2f80c34b88..84689b3ece 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0455.md +++ b/compiler/rustc_error_codes/src/error_codes/E0455.md @@ -4,7 +4,7 @@ as frameworks are specific to that operating system. Erroneous code example: ```ignore (should-compile_fail-but-cannot-doctest-conditionally-without-macos) -#[link(name = "FooCoreServices", kind = "framework")] extern {} +#[link(name = "FooCoreServices", kind = "framework")] extern "C" {} // OS used to compile is Linux for example ``` @@ -12,7 +12,7 @@ To solve this error you can use conditional compilation: ``` #[cfg_attr(target="macos", link(name = "FooCoreServices", kind = "framework"))] -extern {} +extern "C" {} ``` Learn more in the [Conditional Compilation][conditional-compilation] section diff --git a/compiler/rustc_error_codes/src/error_codes/E0458.md b/compiler/rustc_error_codes/src/error_codes/E0458.md index 075226ac98..359aeb6fd9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0458.md +++ b/compiler/rustc_error_codes/src/error_codes/E0458.md @@ -3,7 +3,7 @@ An unknown "kind" was specified for a link attribute. Erroneous code example: ```compile_fail,E0458 -#[link(kind = "wonderful_unicorn")] extern {} +#[link(kind = "wonderful_unicorn")] extern "C" {} // error: unknown kind: `wonderful_unicorn` ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0459.md b/compiler/rustc_error_codes/src/error_codes/E0459.md index 6f75f2a99a..4a49a76544 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0459.md +++ b/compiler/rustc_error_codes/src/error_codes/E0459.md @@ -3,7 +3,7 @@ A link was used without a name parameter. Erroneous code example: ```compile_fail,E0459 -#[link(kind = "dylib")] extern {} +#[link(kind = "dylib")] extern "C" {} // error: `#[link(...)]` specified without `name = "foo"` ``` @@ -11,5 +11,5 @@ Please add the name parameter to allow the rust compiler to find the library you want. Example: ```no_run -#[link(kind = "dylib", name = "some_lib")] extern {} // ok! +#[link(kind = "dylib", name = "some_lib")] extern "C" {} // ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0463.md b/compiler/rustc_error_codes/src/error_codes/E0463.md index e46938c607..d0cd1b1dcb 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0463.md +++ b/compiler/rustc_error_codes/src/error_codes/E0463.md @@ -11,3 +11,24 @@ extern crate cake_is_a_lie; // error: can't find crate for `cake_is_a_lie` You need to link your code to the relevant crate in order to be able to use it (through Cargo or the `-L` option of rustc example). Plugins are crates as well, and you link to them the same way. + +## Common causes + +- The crate is not present at all. If using Cargo, add it to `[dependencies]` + in Cargo.toml. +- The crate is present, but under a different name. If using Cargo, look for + `package = ` under `[dependencies]` in Cargo.toml. + +## Common causes for missing `std` or `core` + +- You are cross-compiling for a target which doesn't have `std` prepackaged. + Consider one of the following: + + Adding a pre-compiled version of std with `rustup target add` + + Building std from source with `cargo build -Z build-std` + + Using `#![no_std]` at the crate root, so you won't need `std` in the first + place. +- You are developing the compiler itself and haven't built libstd from source. + You can usually build it with `x.py build library/std`. More information + about x.py is available in the [rustc-dev-guide]. + +[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html#building-the-compiler diff --git a/compiler/rustc_error_codes/src/error_codes/E0492.md b/compiler/rustc_error_codes/src/error_codes/E0492.md index 1caa59999a..79e7c069a9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0492.md +++ b/compiler/rustc_error_codes/src/error_codes/E0492.md @@ -6,7 +6,7 @@ Erroneous code example: use std::sync::atomic::AtomicUsize; const A: AtomicUsize = AtomicUsize::new(0); -static B: &'static AtomicUsize = &A; +const B: &'static AtomicUsize = &A; // error: cannot borrow a constant which may contain interior mutability, // create a static instead ``` @@ -18,7 +18,7 @@ can't be changed via a shared `&` pointer, but interior mutability would allow it. That is, a constant value could be mutated. On the other hand, a `static` is explicitly a single memory location, which can be mutated at will. -So, in order to solve this error, either use statics which are `Sync`: +So, in order to solve this error, use statics which are `Sync`: ``` use std::sync::atomic::AtomicUsize; diff --git a/compiler/rustc_error_codes/src/error_codes/E0521.md b/compiler/rustc_error_codes/src/error_codes/E0521.md new file mode 100644 index 0000000000..65dcac983a --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0521.md @@ -0,0 +1,28 @@ +Borrowed data escapes outside of closure. + +Erroneous code example: + +```compile_fail,E0521 +let mut list: Vec<&str> = Vec::new(); + +let _add = |el: &str| { + list.push(el); // error: `el` escapes the closure body here +}; +``` + +A type anotation of a closure parameter implies a new lifetime declaration. +Consider to drop it, the compiler is reliably able to infer them. + +``` +let mut list: Vec<&str> = Vec::new(); + +let _add = |el| { + list.push(el); +}; +``` + +See the [Closure type inference and annotation][closure-infere-annotation] and +[Lifetime elision][lifetime-elision] sections of the Book for more details. + +[closure-infere-annotation]: https://doc.rust-lang.org/book/ch13-01-closures.html#closure-type-inference-and-annotation +[lifetime-elision]: https://doc.rust-lang.org/reference/lifetime-elision.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0597.md b/compiler/rustc_error_codes/src/error_codes/E0597.md index 3340768fa8..f6e0b62e1b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0597.md +++ b/compiler/rustc_error_codes/src/error_codes/E0597.md @@ -1,4 +1,4 @@ -This error occurs because a value was dropped while it was still borrowed +This error occurs because a value was dropped while it was still borrowed. Erroneous code example: @@ -15,7 +15,7 @@ let mut x = Foo { x: None }; println!("{:?}", x.x); ``` -In here, `y` is dropped at the end of the inner scope, but it is borrowed by +Here, `y` is dropped at the end of the inner scope, but it is borrowed by `x` until the `println`. To fix the previous example, just remove the scope so that `y` isn't dropped until after the println diff --git a/compiler/rustc_error_codes/src/error_codes/E0617.md b/compiler/rustc_error_codes/src/error_codes/E0617.md index 61b56766c2..1c5d1f87b9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0617.md +++ b/compiler/rustc_error_codes/src/error_codes/E0617.md @@ -3,7 +3,7 @@ Attempted to pass an invalid type of variable into a variadic function. Erroneous code example: ```compile_fail,E0617 -extern { +extern "C" { fn printf(c: *const i8, ...); } @@ -21,7 +21,7 @@ to import from `std::os::raw`). In this case, `c_double` has the same size as `f64` so we can use it directly: ```no_run -# extern { +# extern "C" { # fn printf(c: *const i8, ...); # } unsafe { diff --git a/compiler/rustc_error_codes/src/error_codes/E0633.md b/compiler/rustc_error_codes/src/error_codes/E0633.md index 7f488cde66..4d1f0c4782 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0633.md +++ b/compiler/rustc_error_codes/src/error_codes/E0633.md @@ -6,7 +6,7 @@ Erroneous code example: #![feature(unwind_attributes)] #[unwind()] // error: expected one argument -pub extern fn something() {} +pub extern "C" fn something() {} fn main() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0658.md b/compiler/rustc_error_codes/src/error_codes/E0658.md index d821b9027f..24245a38ae 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0658.md +++ b/compiler/rustc_error_codes/src/error_codes/E0658.md @@ -11,7 +11,7 @@ enum Foo { If you're using a stable or a beta version of rustc, you won't be able to use any unstable features. In order to do so, please switch to a nightly version of -rustc (by using rustup). +rustc (by using [rustup]). If you're using a nightly version of rustc, just add the corresponding feature to be able to use it: @@ -24,3 +24,5 @@ enum Foo { Bar(u64), } ``` + +[rustup]: https://rust-lang.github.io/rustup/concepts/channels.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0724.md b/compiler/rustc_error_codes/src/error_codes/E0724.md index e8f84d0fc7..70578acbe0 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0724.md +++ b/compiler/rustc_error_codes/src/error_codes/E0724.md @@ -18,7 +18,7 @@ the function inside of an `extern` block. ``` #![feature(ffi_returns_twice)] -extern { +extern "C" { #[ffi_returns_twice] // ok! pub fn foo(); } diff --git a/compiler/rustc_error_codes/src/error_codes/E0730.md b/compiler/rustc_error_codes/src/error_codes/E0730.md index 016b3f38aa..56d0e6afa1 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0730.md +++ b/compiler/rustc_error_codes/src/error_codes/E0730.md @@ -3,8 +3,6 @@ An array without a fixed length was pattern-matched. Erroneous code example: ```compile_fail,E0730 -#![feature(const_generics)] - fn is_123(x: [u32; N]) -> bool { match x { [1, 2, ..] => true, // error: cannot pattern-match on an diff --git a/compiler/rustc_error_codes/src/error_codes/E0754.md b/compiler/rustc_error_codes/src/error_codes/E0754.md index 57620bcd65..9f4b19cfda 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0754.md +++ b/compiler/rustc_error_codes/src/error_codes/E0754.md @@ -1,4 +1,4 @@ -An non-ascii identifier was used in an invalid context. +A non-ASCII identifier was used in an invalid context. Erroneous code examples: @@ -13,7 +13,7 @@ fn řųśť() {} // error! fn main() {} ``` -Non-ascii can be used as module names if it is inlined or if a `#[path]` +Non-ASCII can be used as module names if it is inlined or if a `#[path]` attribute is specified. For example: ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0759.md b/compiler/rustc_error_codes/src/error_codes/E0759.md index 6d525310f7..2fe5ada257 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0759.md +++ b/compiler/rustc_error_codes/src/error_codes/E0759.md @@ -27,7 +27,7 @@ fn bar(x: &i32) -> Box { // ok! } ``` -Both [`dyn Trait`] and [`impl Trait`] in return types have a an implicit +Both [`dyn Trait`] and [`impl Trait`] in return types have an implicit `'static` requirement, meaning that the value implementing them that is being returned has to be either a `'static` borrow or an owned value. diff --git a/compiler/rustc_error_codes/src/error_codes/E0770.md b/compiler/rustc_error_codes/src/error_codes/E0770.md index 278bf9b907..b39163a9de 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0770.md +++ b/compiler/rustc_error_codes/src/error_codes/E0770.md @@ -10,6 +10,5 @@ fn foo() {} // error! To fix this error, use a concrete type for the const parameter: ``` -#![feature(const_generics)] fn foo() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0780.md b/compiler/rustc_error_codes/src/error_codes/E0780.md new file mode 100644 index 0000000000..704b4ae181 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0780.md @@ -0,0 +1,19 @@ +Cannot use `doc(inline)` with anonymous imports + +Erroneous code example: + +```ignore (cannot-doctest-multicrate-project) + +#[doc(inline)] // error: invalid doc argument +pub use foo::Foo as _; +``` + +Anonymous imports are always rendered with `#[doc(no_inline)]`. To fix this +error, remove the `#[doc(inline)]` attribute. + +Example: + +```ignore (cannot-doctest-multicrate-project) + +pub use foo::Foo as _; +``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0781.md b/compiler/rustc_error_codes/src/error_codes/E0781.md new file mode 100644 index 0000000000..7641acfb52 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0781.md @@ -0,0 +1,12 @@ +The `C-cmse-nonsecure-call` ABI can only be used with function pointers. + +Erroneous code example: + +```compile_fail,E0781 +#![feature(abi_c_cmse_nonsecure_call)] + +pub extern "C-cmse-nonsecure-call" fn test() {} +``` + +The `C-cmse-nonsecure-call` ABI should be used by casting function pointers to +specific addresses. diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index f165a60336..c09cce21bf 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -36,7 +36,7 @@ macro_rules! forward_inner_docs { ($e:expr => $i:item) => { #[doc = $e] $i - } + }; } /// In general, the `DiagnosticBuilder` uses deref to allow access to @@ -74,11 +74,10 @@ macro_rules! forward { }); }; - // Forward pattern for &mut self -> &mut Self, with S: Into - // type parameter. No obvious way to make this more generic. + // Forward pattern for &mut self -> &mut Self, with generic parameters. ( $(#[$attrs:meta])* - pub fn $n:ident>( + pub fn $n:ident<$($generic:ident: $bound:path),*>( &mut self, $($name:ident: $ty:ty),* $(,)? @@ -86,7 +85,7 @@ macro_rules! forward { ) => { $(#[$attrs])* forward_inner_docs!(concat!("See [`Diagnostic::", stringify!($n), "()`].") => - pub fn $n>(&mut self, $($name: $ty),*) -> &mut Self { + pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self { self.0.diagnostic.$n($($name),*); self }); @@ -398,6 +397,7 @@ impl<'a> DiagnosticBuilder<'a> { self } + forward!(pub fn set_primary_message>(&mut self, msg: M) -> &mut Self); forward!(pub fn set_span>(&mut self, sp: S) -> &mut Self); forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 32104e6f00..ea62e21523 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -644,6 +644,8 @@ impl EmitterWriter { code_offset: usize, margin: Margin, ) { + // Tabs are assumed to have been replaced by spaces in calling code. + debug_assert!(!source_string.contains('\t')); let line_len = source_string.len(); // Create the source line we will highlight. let left = margin.left(line_len); @@ -707,7 +709,7 @@ impl EmitterWriter { } let source_string = match file.get_line(line.line_index - 1) { - Some(s) => s, + Some(s) => replace_tabs(&*s), None => return Vec::new(), }; @@ -1376,8 +1378,17 @@ impl EmitterWriter { let file = annotated_file.file.clone(); let line = &annotated_file.lines[line_idx]; if let Some(source_string) = file.get_line(line.line_index - 1) { - let leading_whitespace = - source_string.chars().take_while(|c| c.is_whitespace()).count(); + let leading_whitespace = source_string + .chars() + .take_while(|c| c.is_whitespace()) + .map(|c| { + match c { + // Tabs are displayed as 4 spaces + '\t' => 4, + _ => 1, + } + }) + .sum(); if source_string.chars().any(|c| !c.is_whitespace()) { whitespace_margin = min(whitespace_margin, leading_whitespace); } @@ -1502,7 +1513,7 @@ impl EmitterWriter { self.draw_line( &mut buffer, - &unannotated_line, + &replace_tabs(&unannotated_line), annotated_file.lines[line_idx + 1].line_index - 1, last_buffer_line_num, width_offset, @@ -1598,7 +1609,7 @@ impl EmitterWriter { ); // print the suggestion draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); - buffer.append(row_num, line, Style::NoStyle); + buffer.append(row_num, &replace_tabs(line), Style::NoStyle); row_num += 1; } @@ -1930,6 +1941,10 @@ impl FileWithAnnotatedLines { } } +fn replace_tabs(str: &str) -> String { + str.replace('\t', " ") +} + fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { buffer.puts(line, col, "| ", Style::LineNumber); } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 593e0d9203..aa88233209 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -804,7 +804,7 @@ impl HandlerInner { } fn treat_err_as_bug(&self) -> bool { - self.flags.treat_err_as_bug.map(|c| self.err_count() >= c).unwrap_or(false) + self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() >= c) } fn print_error_count(&mut self, registry: &Registry) { @@ -901,7 +901,7 @@ impl HandlerInner { fn span_bug(&mut self, sp: impl Into, msg: &str) -> ! { self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp); - panic!(ExplicitBug); + panic::panic_any(ExplicitBug); } fn emit_diag_at_span(&mut self, mut diag: Diagnostic, sp: impl Into) { @@ -913,7 +913,7 @@ impl HandlerInner { // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before // incrementing `err_count` by one, so we need to +1 the comparing. // FIXME: Would be nice to increment err_count in a more coherent way. - if self.flags.treat_err_as_bug.map(|c| self.err_count() + 1 >= c).unwrap_or(false) { + if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c) { // FIXME: don't abort here if report_delayed_bugs is off self.span_bug(sp, msg); } @@ -955,7 +955,7 @@ impl HandlerInner { fn bug(&mut self, msg: &str) -> ! { self.emit_diagnostic(&Diagnostic::new(Bug, msg)); - panic!(ExplicitBug); + panic::panic_any(ExplicitBug); } fn delay_as_bug(&mut self, diagnostic: Diagnostic) { diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index dbb2523f28..acb88e57db 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -122,11 +122,13 @@ impl Annotation { } pub fn is_multiline(&self) -> bool { - matches!(self.annotation_type, + matches!( + self.annotation_type, AnnotationType::Multiline(_) - | AnnotationType::MultilineStart(_) - | AnnotationType::MultilineLine(_) - | AnnotationType::MultilineEnd(_)) + | AnnotationType::MultilineStart(_) + | AnnotationType::MultilineLine(_) + | AnnotationType::MultilineEnd(_) + ) } pub fn len(&self) -> usize { @@ -158,7 +160,10 @@ impl Annotation { pub fn takes_space(&self) -> bool { // Multiline annotations always have to keep vertical space. - matches!(self.annotation_type, AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_)) + matches!( + self.annotation_type, + AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_) + ) } } diff --git a/compiler/rustc_errors/src/styled_buffer.rs b/compiler/rustc_errors/src/styled_buffer.rs index f2d255d7d9..ef71ee36ea 100644 --- a/compiler/rustc_errors/src/styled_buffer.rs +++ b/compiler/rustc_errors/src/styled_buffer.rs @@ -13,34 +13,13 @@ impl StyledBuffer { StyledBuffer { text: vec![], styles: vec![] } } - fn replace_tabs(&mut self) { - for (line_pos, line) in self.text.iter_mut().enumerate() { - let mut tab_pos = vec![]; - for (pos, c) in line.iter().enumerate() { - if *c == '\t' { - tab_pos.push(pos); - } - } - // start with the tabs at the end of the line to replace them with 4 space chars - for pos in tab_pos.iter().rev() { - assert_eq!(line.remove(*pos), '\t'); - // fix the position of the style to match up after replacing the tabs - let s = self.styles[line_pos].remove(*pos); - for _ in 0..4 { - line.insert(*pos, ' '); - self.styles[line_pos].insert(*pos, s); - } - } - } - } + pub fn render(&self) -> Vec> { + // Tabs are assumed to have been replaced by spaces in calling code. + debug_assert!(self.text.iter().all(|r| !r.contains(&'\t'))); - pub fn render(&mut self) -> Vec> { let mut output: Vec> = vec![]; let mut styled_vec: Vec = vec![]; - // before we render, replace tabs with spaces - self.replace_tabs(); - for (row, row_style) in self.text.iter().zip(&self.styles) { let mut current_style = Style::NoStyle; let mut current_text = String::new(); diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 25c2851f6d..7413b0d943 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -18,6 +18,7 @@ rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } +rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_lexer = { path = "../rustc_lexer" } rustc_parse = { path = "../rustc_parse" } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 335f3b7a9a..08543d1622 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -2,8 +2,8 @@ use crate::expand::{self, AstFragment, Invocation}; use crate::module::DirectoryOwnership; use rustc_ast::ptr::P; -use rustc_ast::token; -use rustc_ast::tokenstream::TokenStream; +use rustc_ast::token::{self, Nonterminal}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream}; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, Attribute, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, HasAttrs, Stability}; @@ -12,7 +12,7 @@ use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{DiagnosticBuilder, ErrorReported}; use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS}; use rustc_session::{parse::ParseSess, Limit, Session}; -use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::def_id::DefId; use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind}; use rustc_span::source_map::SourceMap; @@ -119,8 +119,8 @@ impl Annotatable { } } - crate fn into_tokens(self, sess: &ParseSess) -> TokenStream { - let nt = match self { + crate fn into_nonterminal(self) -> Nonterminal { + match self { Annotatable::Item(item) => token::NtItem(item), Annotatable::TraitItem(item) | Annotatable::ImplItem(item) => { token::NtItem(P(item.and_then(ast::AssocItem::into_item))) @@ -137,8 +137,11 @@ impl Annotatable { | Annotatable::Param(..) | Annotatable::StructField(..) | Annotatable::Variant(..) => panic!("unexpected annotatable"), - }; - nt_to_tokenstream(&nt, sess, DUMMY_SP) + } + } + + crate fn into_tokens(self, sess: &ParseSess) -> TokenStream { + nt_to_tokenstream(&self.into_nonterminal(), sess, CanSynthesizeMissingTokens::No) } pub fn expect_item(self) -> P { @@ -725,9 +728,7 @@ pub struct SyntaxExtension { pub edition: Edition, /// Built-in macros have a couple of special properties like availability /// in `#[no_implicit_prelude]` modules, so we have to keep this flag. - pub is_builtin: bool, - /// We have to identify macros providing a `Copy` impl early for compatibility reasons. - pub is_derive_copy: bool, + pub builtin_name: Option, } impl SyntaxExtension { @@ -755,8 +756,7 @@ impl SyntaxExtension { deprecation: None, helper_attrs: Vec::new(), edition, - is_builtin: false, - is_derive_copy: false, + builtin_name: None, kind, } } @@ -782,7 +782,9 @@ impl SyntaxExtension { } } - let is_builtin = sess.contains_name(attrs, sym::rustc_builtin_macro); + let builtin_name = sess + .find_by_name(attrs, sym::rustc_builtin_macro) + .map(|a| a.value_str().unwrap_or(name)); let (stability, const_stability) = attr::find_stability(&sess, attrs, span); if const_stability.is_some() { sess.parse_sess @@ -800,8 +802,7 @@ impl SyntaxExtension { deprecation: attr::find_deprecation(&sess, attrs).map(|(d, _)| d), helper_attrs, edition, - is_builtin, - is_derive_copy: is_builtin && name == sym::Copy, + builtin_name, } } @@ -839,19 +840,17 @@ impl SyntaxExtension { descr: Symbol, macro_def_id: Option, ) -> ExpnData { - ExpnData { - kind: ExpnKind::Macro(self.macro_kind(), descr), + ExpnData::new( + ExpnKind::Macro(self.macro_kind(), descr), parent, call_site, - def_site: self.span, - allow_internal_unstable: self.allow_internal_unstable.clone(), - allow_internal_unsafe: self.allow_internal_unsafe, - local_inner_macros: self.local_inner_macros, - edition: self.edition, + self.span, + self.allow_internal_unstable.clone(), + self.allow_internal_unsafe, + self.local_inner_macros, + self.edition, macro_def_id, - krate: LOCAL_CRATE, - orig_id: None, - } + ) } } @@ -869,7 +868,7 @@ pub trait ResolverExpand { fn resolve_dollar_crates(&mut self); fn visit_ast_fragment_with_placeholders(&mut self, expn_id: ExpnId, fragment: &AstFragment); - fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension); + fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind); fn expansion_for_ast_pass( &mut self, @@ -930,7 +929,9 @@ pub struct ExtCtxt<'a> { pub force_mode: bool, pub expansions: FxHashMap>, /// Called directly after having parsed an external `mod foo;` in expansion. - pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>, + /// + /// `Ident` is the module name. + pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate, Ident)>, } impl<'a> ExtCtxt<'a> { @@ -938,7 +939,7 @@ impl<'a> ExtCtxt<'a> { sess: &'a Session, ecfg: expand::ExpansionConfig<'a>, resolver: &'a mut dyn ResolverExpand, - extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>, + extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate, Ident)>, ) -> ExtCtxt<'a> { ExtCtxt { sess, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 563783c5b7..b07bce9487 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -29,6 +29,7 @@ use smallvec::SmallVec; pub struct StripUnconfigured<'a> { pub sess: &'a Session, pub features: Option<&'a Features>, + pub modified: bool, } fn get_features( @@ -199,7 +200,7 @@ fn get_features( // `cfg_attr`-process the crate's attributes and compute the crate's features. pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features) { - let mut strip_unconfigured = StripUnconfigured { sess, features: None }; + let mut strip_unconfigured = StripUnconfigured { sess, features: None, modified: false }; let unconfigured_attrs = krate.attrs.clone(); let diag = &sess.parse_sess.span_diagnostic; @@ -243,7 +244,12 @@ const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ impl<'a> StripUnconfigured<'a> { pub fn configure(&mut self, mut node: T) -> Option { self.process_cfg_attrs(&mut node); - self.in_cfg(node.attrs()).then_some(node) + if self.in_cfg(node.attrs()) { + Some(node) + } else { + self.modified = true; + None + } } /// Parse and expand all `cfg_attr` attributes into a list of attributes @@ -270,6 +276,9 @@ impl<'a> StripUnconfigured<'a> { return vec![attr]; } + // A `#[cfg_attr]` either gets removed, or replaced with a new attribute + self.modified = true; + let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) { None => return vec![], Some(r) => r, @@ -414,7 +423,7 @@ impl<'a> StripUnconfigured<'a> { /// If attributes are not allowed on expressions, emit an error for `attr` pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { - if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) { + if !self.features.map_or(true, |features| features.stmt_expr_attributes) { let mut err = feature_err( &self.sess.parse_sess, sym::stmt_expr_attributes, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 2da5bde028..5fdb7fc591 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -12,7 +12,7 @@ use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor}; -use rustc_ast::{self as ast, AttrItem, Block, LitKind, NodeId, PatKind, Path}; +use rustc_ast::{self as ast, AttrItem, AttrStyle, Block, LitKind, NodeId, PatKind, Path}; use rustc_ast::{ItemKind, MacArgs, MacCallStmt, MacStmtStyle, StmtKind, Unsafe}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, is_builtin_attr, HasAttrs}; @@ -20,7 +20,7 @@ use rustc_data_structures::map_in_place::MapInPlace; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{struct_span_err, Applicability, PResult}; use rustc_feature::Features; -use rustc_parse::parser::{AttemptLocalParseRecovery, Parser}; +use rustc_parse::parser::{AttemptLocalParseRecovery, ForceCollect, Parser}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; use rustc_session::lint::BuiltinLintDiagnostics; @@ -522,12 +522,29 @@ impl<'a, 'b> MacroExpander<'a, 'b> { item.visit_attrs(|attrs| attrs.retain(|a| !a.has_name(sym::derive))); (item, Vec::new()) } else { - let mut item = StripUnconfigured { + let mut visitor = StripUnconfigured { sess: self.cx.sess, features: self.cx.ecfg.features, - } - .fully_configure(item); + modified: false, + }; + let mut item = visitor.fully_configure(item); item.visit_attrs(|attrs| attrs.retain(|a| !a.has_name(sym::derive))); + if visitor.modified && !derives.is_empty() { + // Erase the tokens if cfg-stripping modified the item + // This will cause us to synthesize fake tokens + // when `nt_to_tokenstream` is called on this item. + match &mut item { + Annotatable::Item(item) => item.tokens = None, + Annotatable::Stmt(stmt) => { + if let StmtKind::Item(item) = &mut stmt.kind { + item.tokens = None + } else { + panic!("Unexpected stmt {:?}", stmt); + } + } + _ => panic!("Unexpected annotatable {:?}", item), + } + } invocations.reserve(derives.len()); let derive_placeholders = derives @@ -622,7 +639,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let invocations = { let mut collector = InvocationCollector { - cfg: StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features }, + cfg: StripUnconfigured { + sess: &self.cx.sess, + features: self.cx.ecfg.features, + modified: false, + }, cx: self.cx, invocations: Vec::new(), monotonic: self.monotonic, @@ -716,7 +737,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { SyntaxExtensionKind::Attr(expander) => { self.gate_proc_macro_input(&item); self.gate_proc_macro_attr_item(span, &item); - let tokens = item.into_tokens(&self.cx.sess.parse_sess); + let tokens = match attr.style { + AttrStyle::Outer => item.into_tokens(&self.cx.sess.parse_sess), + // FIXME: Properly collect tokens for inner attributes + AttrStyle::Inner => rustc_parse::fake_token_stream( + &self.cx.sess.parse_sess, + &item.into_nonterminal(), + ), + }; let attr_item = attr.unwrap_normal_item(); if let MacArgs::Eq(..) = attr_item.args { self.cx.span_err(span, "key-value macro attributes are not supported"); @@ -868,7 +896,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { fragment } Err(mut err) => { - err.set_span(span); + if err.span.is_dummy() { + err.set_span(span); + } annotate_err_with_kind(&mut err, kind, span); err.emit(); self.cx.trace_macros_diag(); @@ -885,7 +915,7 @@ pub fn parse_ast_fragment<'a>( Ok(match kind { AstFragmentKind::Items => { let mut items = SmallVec::new(); - while let Some(item) = this.parse_item()? { + while let Some(item) = this.parse_item(ForceCollect::No)? { items.push(item); } AstFragment::Items(items) @@ -991,15 +1021,16 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // with exception of the derive container case which is not resolved and can get // its expansion data immediately. let expn_data = match &kind { - InvocationKind::DeriveContainer { item, .. } => Some(ExpnData { - parent: self.cx.current_expansion.id, - ..ExpnData::default( + InvocationKind::DeriveContainer { item, .. } => { + let mut expn_data = ExpnData::default( ExpnKind::Macro(MacroKind::Attr, sym::derive), item.span(), self.cx.sess.parse_sess.edition, None, - ) - }), + ); + expn_data.parent = self.cx.current_expansion.id; + Some(expn_data) + } _ => None, }; let expn_id = ExpnId::fresh(expn_data); @@ -1378,7 +1409,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { proc_macros: vec![], }; if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded { - extern_mod_loaded(&krate); + extern_mod_loaded(&krate, ident); } *old_mod = krate.module; @@ -1514,13 +1545,8 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } fn visit_item_kind(&mut self, item: &mut ast::ItemKind) { - match item { - ast::ItemKind::MacroDef(..) => {} - _ => { - self.cfg.configure_item_kind(item); - noop_visit_item_kind(item, self); - } - } + self.cfg.configure_item_kind(item); + noop_visit_item_kind(item, self); } fn flat_map_generic_param( diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 0c44f5fe9e..1aed42a24e 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -77,9 +77,9 @@ use TokenTreeOrTokenTreeSlice::*; use crate::mbe::{self, TokenTree}; use rustc_ast::token::{self, DocComment, Nonterminal, Token}; -use rustc_parse::parser::{OrPatNonterminalMode, Parser}; +use rustc_parse::parser::Parser; use rustc_session::parse::ParseSess; -use rustc_span::{edition::Edition, symbol::MacroRulesNormalizedIdent}; +use rustc_span::symbol::MacroRulesNormalizedIdent; use smallvec::{smallvec, SmallVec}; @@ -419,18 +419,6 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { } } -/// In edition 2015/18, `:pat` can only match `pat` because otherwise, we have -/// breakage. As of edition 2021, `:pat` matches `top_pat`. -/// -/// See for more info. -fn or_pat_mode(edition: Edition) -> OrPatNonterminalMode { - match edition { - Edition::Edition2015 | Edition::Edition2018 => OrPatNonterminalMode::NoTopAlt, - // FIXME(mark-i-m): uncomment this when edition 2021 machinery is added. - // Edition::Edition2021 => OrPatNonterminalMode::TopPat, - } -} - /// Process the matcher positions of `cur_items` until it is empty. In the process, this will /// produce more items in `next_items`, `eof_items`, and `bb_items`. /// @@ -512,7 +500,7 @@ fn inner_parse_loop<'root, 'tt>( if idx == len && item.sep.is_some() { // We have a separator, and it is the current token. We can advance past the // separator token. - if item.sep.as_ref().map(|sep| token_name_eq(token, sep)).unwrap_or(false) { + if item.sep.as_ref().map_or(false, |sep| token_name_eq(token, sep)) { item.idx += 1; next_items.push(item); } @@ -578,14 +566,13 @@ fn inner_parse_loop<'root, 'tt>( // We need to match a metavar with a valid ident... call out to the black-box // parser by adding an item to `bb_items`. - TokenTree::MetaVarDecl(span, _, Some(kind)) => { + TokenTree::MetaVarDecl(_, _, Some(kind)) => { // Built-in nonterminals never start with these tokens, so we can eliminate // them from consideration. // // We use the span of the metavariable declaration to determine any // edition-specific matching behavior for non-terminals. - if Parser::nonterminal_may_begin_with(kind, token, or_pat_mode(span.edition())) - { + if Parser::nonterminal_may_begin_with(kind, token) { bb_items.push(item); } } @@ -749,8 +736,7 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na let match_cur = item.match_cur; // We use the span of the metavariable declaration to determine any // edition-specific matching behavior for non-terminals. - let nt = match parser.to_mut().parse_nonterminal(kind, or_pat_mode(span.edition())) - { + let nt = match parser.to_mut().parse_nonterminal(kind) { Err(mut err) => { err.span_label( span, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 89d375b257..73fbde70bd 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -11,12 +11,14 @@ use crate::mbe::transcribe::transcribe; use rustc_ast as ast; use rustc_ast::token::{self, NonterminalKind, NtTT, Token, TokenKind::*}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; +use rustc_ast::NodeId; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_feature::Features; +use rustc_lint_defs::builtin::SEMICOLON_IN_EXPRESSIONS_FROM_MACROS; use rustc_parse::parser::Parser; use rustc_session::parse::ParseSess; use rustc_session::Session; @@ -37,6 +39,7 @@ crate struct ParserAnyMacro<'a> { site_span: Span, /// The ident of the macro we're parsing macro_ident: Ident, + lint_node_id: NodeId, arm_span: Span, } @@ -56,36 +59,11 @@ crate fn annotate_err_with_kind( }; } -/// Instead of e.g. `vec![a, b, c]` in a pattern context, suggest `[a, b, c]`. -fn suggest_slice_pat(e: &mut DiagnosticBuilder<'_>, site_span: Span, parser: &Parser<'_>) { - let mut suggestion = None; - if let Ok(code) = parser.sess.source_map().span_to_snippet(site_span) { - if let Some(bang) = code.find('!') { - suggestion = Some(code[bang + 1..].to_string()); - } - } - if let Some(suggestion) = suggestion { - e.span_suggestion( - site_span, - "use a slice pattern here instead", - suggestion, - Applicability::MachineApplicable, - ); - } else { - e.span_label(site_span, "use a slice pattern here instead"); - } - e.help( - "for more information, see https://doc.rust-lang.org/edition-guide/\ - rust-2018/slice-patterns.html", - ); -} - fn emit_frag_parse_err( mut e: DiagnosticBuilder<'_>, parser: &Parser<'_>, orig_parser: &mut Parser<'_>, site_span: Span, - macro_ident: Ident, arm_span: Span, kind: AstFragmentKind, ) { @@ -113,9 +91,6 @@ fn emit_frag_parse_err( e.span_label(site_span, "in this macro invocation"); } match kind { - AstFragmentKind::Pat if macro_ident.name == sym::vec => { - suggest_slice_pat(&mut e, site_span, parser); - } // Try a statement if an expression is wanted but failed and suggest adding `;` to call. AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) { Err(mut err) => err.cancel(), @@ -138,12 +113,13 @@ fn emit_frag_parse_err( impl<'a> ParserAnyMacro<'a> { crate fn make(mut self: Box>, kind: AstFragmentKind) -> AstFragment { - let ParserAnyMacro { site_span, macro_ident, ref mut parser, arm_span } = *self; + let ParserAnyMacro { site_span, macro_ident, ref mut parser, lint_node_id, arm_span } = + *self; let snapshot = &mut parser.clone(); let fragment = match parse_ast_fragment(parser, kind) { Ok(f) => f, Err(err) => { - emit_frag_parse_err(err, parser, snapshot, site_span, macro_ident, arm_span, kind); + emit_frag_parse_err(err, parser, snapshot, site_span, arm_span, kind); return kind.dummy(site_span); } }; @@ -152,6 +128,12 @@ impl<'a> ParserAnyMacro<'a> { // `macro_rules! m { () => { panic!(); } }` isn't parsed by `.parse_expr()`, // but `m!()` is allowed in expression positions (cf. issue #34706). if kind == AstFragmentKind::Expr && parser.token == token::Semi { + parser.sess.buffer_lint( + SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, + parser.token.span, + lint_node_id, + "trailing semicolon in macro used in expression position", + ); parser.bump(); } @@ -203,7 +185,7 @@ fn macro_rules_dummy_expander<'cx>( } fn trace_macros_note(cx_expansions: &mut FxHashMap>, sp: Span, message: String) { - let sp = sp.macro_backtrace().last().map(|trace| trace.call_site).unwrap_or(sp); + let sp = sp.macro_backtrace().last().map_or(sp, |trace| trace.call_site); cx_expansions.entry(sp).or_default().push(message); } @@ -304,6 +286,7 @@ fn generic_extension<'cx>( let mut p = Parser::new(sess, tts, false, None); p.last_type_ascription = cx.current_expansion.prior_type_ascription; + let lint_node_id = cx.resolver.lint_node_id(cx.current_expansion.id); // Let the context choose how to interpret the result. // Weird, but useful for X-macros. @@ -315,6 +298,7 @@ fn generic_extension<'cx>( // macro leaves unparsed tokens. site_span: sp, macro_ident: name, + lint_node_id, arm_span, }); } @@ -476,10 +460,15 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - let tt = - mbe::quoted::parse(tt.clone().into(), true, &sess.parse_sess, def.id) - .pop() - .unwrap(); + let tt = mbe::quoted::parse( + tt.clone().into(), + true, + &sess.parse_sess, + def.id, + features, + ) + .pop() + .unwrap(); valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def.attrs, &tt); return tt; } @@ -501,6 +490,7 @@ pub fn compile_declarative_macro( false, &sess.parse_sess, def.id, + features, ) .pop() .unwrap(); @@ -1090,7 +1080,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { _ => IsInFollow::No(TOKENS), } } - NonterminalKind::Pat => { + NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => { const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; match tok { TokenTree::Token(token) => match token.kind { diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 01b11bb979..c8049495d2 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -5,8 +5,9 @@ use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; -use rustc_session::parse::ParseSess; -use rustc_span::symbol::{kw, Ident}; +use rustc_feature::Features; +use rustc_session::parse::{feature_err, ParseSess}; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; @@ -29,10 +30,8 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ /// `ident` are "matchers". They are not present in the body of a macro rule -- just in the /// pattern, so we pass a parameter to indicate whether to expect them or not. /// - `sess`: the parsing session. Any errors will be emitted to this session. -/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use -/// unstable features or not. -/// - `edition`: which edition are we in. -/// - `macro_node_id`: the NodeId of the macro we are parsing. +/// - `node_id`: the NodeId of the macro we are parsing. +/// - `features`: language features so we can do feature gating. /// /// # Returns /// @@ -42,6 +41,7 @@ pub(super) fn parse( expect_matchers: bool, sess: &ParseSess, node_id: NodeId, + features: &Features, ) -> Vec { // Will contain the final collection of `self::TokenTree` let mut result = Vec::new(); @@ -52,7 +52,7 @@ pub(super) fn parse( while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). - let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id); + let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id, features); match tree { TokenTree::MetaVar(start_sp, ident) if expect_matchers => { let span = match trees.next() { @@ -61,27 +61,48 @@ pub(super) fn parse( Some(tokenstream::TokenTree::Token(token)) => match token.ident() { Some((frag, _)) => { let span = token.span.with_lo(start_sp.lo()); - let kind = token::NonterminalKind::from_symbol(frag.name) - .unwrap_or_else(|| { - let msg = format!( - "invalid fragment specifier `{}`", - frag.name - ); - sess.span_diagnostic - .struct_span_err(span, &msg) - .help(VALID_FRAGMENT_NAMES_MSG) + + match frag.name { + sym::pat2018 | sym::pat2021 => { + if !features.edition_macro_pats { + feature_err( + sess, + sym::edition_macro_pats, + frag.span, + "`pat2018` and `pat2021` are unstable.", + ) .emit(); - token::NonterminalKind::Ident - }); + } + } + _ => {} + } + + let kind = + token::NonterminalKind::from_symbol(frag.name, || { + span.edition() + }) + .unwrap_or_else( + || { + let msg = format!( + "invalid fragment specifier `{}`", + frag.name + ); + sess.span_diagnostic + .struct_span_err(span, &msg) + .help(VALID_FRAGMENT_NAMES_MSG) + .emit(); + token::NonterminalKind::Ident + }, + ); result.push(TokenTree::MetaVarDecl(span, ident, Some(kind))); continue; } _ => token.span, }, - tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(span), + tree => tree.as_ref().map_or(span, tokenstream::TokenTree::span), } } - tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp), + tree => tree.as_ref().map_or(start_sp, tokenstream::TokenTree::span), }; if node_id != DUMMY_NODE_ID { // Macros loaded from other crates have dummy node ids. @@ -110,14 +131,14 @@ pub(super) fn parse( /// converting `tree` /// - `expect_matchers`: same as for `parse` (see above). /// - `sess`: the parsing session. Any errors will be emitted to this session. -/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use -/// unstable features or not. +/// - `features`: language features so we can do feature gating. fn parse_tree( tree: tokenstream::TokenTree, outer_trees: &mut impl Iterator, expect_matchers: bool, sess: &ParseSess, node_id: NodeId, + features: &Features, ) -> TokenTree { // Depending on what `tree` is, we could be parsing different parts of a macro match tree { @@ -145,7 +166,7 @@ fn parse_tree( sess.span_diagnostic.span_err(span.entire(), &msg); } // Parse the contents of the sequence itself - let sequence = parse(tts, expect_matchers, sess, node_id); + let sequence = parse(tts, expect_matchers, sess, node_id, features); // Get the Kleene operator and optional separator let (separator, kleene) = parse_sep_and_kleene_op(&mut trees, span.entire(), sess); @@ -196,7 +217,10 @@ fn parse_tree( // descend into the delimited set and further parse it. tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited( span, - Lrc::new(Delimited { delim, tts: parse(tts, expect_matchers, sess, node_id) }), + Lrc::new(Delimited { + delim, + tts: parse(tts, expect_matchers, sess, node_id, features), + }), ), } } @@ -226,7 +250,7 @@ fn parse_kleene_op( Some(op) => Ok(Ok((op, token.span))), None => Ok(Err(token)), }, - tree => Err(tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(span)), + tree => Err(tree.as_ref().map_or(span, tokenstream::TokenTree::span)), } } diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs index 643305f153..f4fcaf5c0a 100644 --- a/compiler/rustc_expand/src/parse/tests.rs +++ b/compiler/rustc_expand/src/parse/tests.rs @@ -8,6 +8,7 @@ use rustc_ast::{self as ast, PatKind}; use rustc_ast_pretty::pprust::item_to_string; use rustc_errors::PResult; use rustc_parse::new_parser_from_source_str; +use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; use rustc_span::source_map::FilePathMapping; use rustc_span::symbol::{kw, sym, Symbol}; @@ -29,7 +30,7 @@ fn parse_item_from_source_str( source: String, sess: &ParseSess, ) -> PResult<'_, Option>> { - new_parser_from_source_str(sess, name, source).parse_item() + new_parser_from_source_str(sess, name, source).parse_item(ForceCollect::No) } // Produces a `rustc_span::span`. @@ -44,7 +45,7 @@ fn string_to_expr(source_str: String) -> P { /// Parses a string, returns an item. fn string_to_item(source_str: String) -> Option> { - with_error_checking_parse(source_str, &sess(), |p| p.parse_item()) + with_error_checking_parse(source_str, &sess(), |p| p.parse_item(ForceCollect::No)) } #[should_panic] diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index ce19e813bb..d040539cd7 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -258,12 +258,9 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> { fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { match item.kind { - ast::ItemKind::MacCall(_) => return self.remove(item.id).make_items(), - ast::ItemKind::MacroDef(_) => return smallvec![item], - _ => {} + ast::ItemKind::MacCall(_) => self.remove(item.id).make_items(), + _ => noop_flat_map_item(item, self), } - - noop_flat_map_item(item, self) } fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 36707a1ae2..6779734cfc 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -3,12 +3,13 @@ use crate::proc_macro_server; use rustc_ast::ptr::P; use rustc_ast::token; -use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree}; use rustc_ast::{self as ast, *}; use rustc_data_structures::sync::Lrc; use rustc_errors::{struct_span_err, Applicability, ErrorReported}; use rustc_lexer::is_ident; use rustc_parse::nt_to_tokenstream; +use rustc_parse::parser::ForceCollect; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; @@ -94,7 +95,7 @@ impl MultiItemModifier for ProcMacroDerive { let input = if item.pretty_printing_compatibility_hack() { TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into() } else { - nt_to_tokenstream(&item, &ecx.sess.parse_sess, DUMMY_SP) + nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::Yes) }; let server = proc_macro_server::Rustc::new(ecx); @@ -117,7 +118,7 @@ impl MultiItemModifier for ProcMacroDerive { let mut items = vec![]; loop { - match parser.parse_item() { + match parser.parse_item(ForceCollect::No) { Ok(None) => break, Ok(Some(item)) => { if is_stmt { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 4cfb188783..b6195d3bbc 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -2,7 +2,8 @@ use crate::base::ExtCtxt; use rustc_ast as ast; use rustc_ast::token; -use rustc_ast::tokenstream::{self, DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; +use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens}; +use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::Diagnostic; @@ -178,7 +179,7 @@ impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec)> { TokenTree::Ident(Ident::new(sess, name.name, is_raw, name.span)) } else { - let stream = nt_to_tokenstream(&nt, sess, span); + let stream = nt_to_tokenstream(&nt, sess, CanSynthesizeMissingTokens::No); TokenTree::Group(Group { delimiter: Delimiter::None, stream, diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 6993ce58fa..f2345ff270 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -92,7 +92,7 @@ crate fn matches_codepattern(a: &str, b: &str) -> bool { /// Advances the given peekable `Iterator` until it reaches a non-whitespace character. fn scan_for_non_ws_or_end>(iter: &mut Peekable) { - while iter.peek().copied().map(|c| rustc_lexer::is_whitespace(c)) == Some(true) { + while iter.peek().copied().map(rustc_lexer::is_whitespace) == Some(true) { iter.next(); } } diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 4401ec0a04..aa54ffb132 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -273,6 +273,8 @@ declare_features! ( /// Allows patterns with concurrent by-move and by-ref bindings. /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref. (accepted, move_ref_pattern, "1.48.0", Some(68354), None), + /// The smallest useful subset of `const_generics`. + (accepted, min_const_generics, "1.51.0", Some(74878), None), // ------------------------------------------------------------------------- // feature-group-end: accepted features diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 845e03150d..4f38e06002 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -36,7 +36,7 @@ macro_rules! declare_features { ),+]; /// A set of features to be used by later passes. - #[derive(Clone, Default)] + #[derive(Clone, Default, Debug)] pub struct Features { /// `#![feature]` attrs for language features, for error reporting. pub declared_lang_features: Vec<(Symbol, Span, Option)>, @@ -485,9 +485,6 @@ declare_features! ( /// Allows `async || body` closures. (active, async_closure, "1.37.0", Some(62290), None), - /// Allows `[x; N]` where `x` is a constant (RFC 2203). - (active, const_in_array_repeat_expressions, "1.37.0", Some(49147), None), - /// Allows `impl Trait` to be used inside type aliases (RFC 2515). (active, type_alias_impl_trait, "1.38.0", Some(63063), None), @@ -578,13 +575,10 @@ declare_features! ( /// Allows calling `transmute` in const fn (active, const_fn_transmute, "1.46.0", Some(53605), None), - /// The smallest useful subset of `const_generics`. - (active, min_const_generics, "1.47.0", Some(74878), None), - /// Allows `if let` guard in match arms. (active, if_let_guard, "1.47.0", Some(51114), None), - /// Allows non-trivial generic constants which have to be manually propageted upwards. + /// Allows non-trivial generic constants which have to be manually propagated upwards. (active, const_evaluatable_checked, "1.48.0", Some(76560), None), /// Allows basic arithmetic on floating point types in a `const fn`. @@ -623,6 +617,23 @@ declare_features! ( /// Allows arbitrary expressions in key-value attributes at parse time. (active, extended_key_value_attributes, "1.50.0", Some(78835), None), + /// `:pat2018` and `:pat2021` macro matchers. + (active, edition_macro_pats, "1.51.0", Some(54883), None), + + /// Allows const generics to have default values (e.g. `struct Foo(...);`). + (active, const_generics_defaults, "1.51.0", Some(44580), None), + + /// Allows references to types with interior mutability within constants + (active, const_refs_to_cell, "1.51.0", Some(80384), None), + + /// Allows using `pointer` and `reference` in intra-doc links + (active, intra_doc_pointers, "1.51.0", Some(80896), None), + + /// Allows `extern "C-cmse-nonsecure-call" fn()`. + (active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None), + + /// Lessens the requirements for structs to implement `Unsize`. + (active, relaxed_struct_unsize, "1.51.0", Some(1), None), // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -647,9 +658,12 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::repr128, sym::unsized_locals, sym::capture_disjoint_fields, + sym::const_generics_defaults, ]; /// Some features are not allowed to be used together at the same time, if /// the two are present, produce an error. -pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = - &[(sym::const_generics, sym::min_const_generics)]; +/// +/// Currently empty, but we will probably need this again in the future, +/// so let's keep it in for now. +pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[]; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index fa8edba629..3ed5320da7 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -442,7 +442,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Internal attributes, Macro related: // ========================================================================== - rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word), IMPL_DETAIL), + rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word, NameValueStr: "name"), IMPL_DETAIL), rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE), rustc_attr!( rustc_macro_transparency, AssumedUsed, diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 07bd1602cd..38a3a4e3d4 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -97,6 +97,9 @@ declare_features! ( (removed, extern_in_paths, "1.33.0", Some(55600), None, Some("subsumed by `::foo::bar` paths")), (removed, quote, "1.33.0", Some(29601), None, None), + /// Allows `[x; N]` where `x` is a constant (RFC 2203). + (removed, const_in_array_repeat_expressions, "1.37.0", Some(49147), None, + Some("removed due to causing promotable bugs")), /// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238). (removed, dropck_parametricity, "1.38.0", Some(28498), None, None), (removed, await_macro, "1.38.0", Some(50547), None, diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index 7742961e65..87e97c746e 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -62,8 +62,10 @@ pub enum LinkOrCopy { pub fn link_or_copy, Q: AsRef>(p: P, q: Q) -> io::Result { let p = p.as_ref(); let q = q.as_ref(); - if q.exists() { - fs::remove_file(&q)?; + match fs::remove_file(&q) { + Ok(()) => (), + Err(err) if err.kind() == io::ErrorKind::NotFound => (), + Err(err) => return Err(err), } match fs::hard_link(p, q) { diff --git a/compiler/rustc_graphviz/src/tests.rs b/compiler/rustc_graphviz/src/tests.rs index 055e13156a..70b8197f5e 100644 --- a/compiler/rustc_graphviz/src/tests.rs +++ b/compiler/rustc_graphviz/src/tests.rs @@ -55,7 +55,7 @@ impl NodeLabels<&'static str> { fn to_opt_strs(self) -> Vec> { match self { UnlabelledNodes(len) => vec![None; len], - AllNodesLabelled(lbls) => lbls.into_iter().map(|l| Some(l)).collect(), + AllNodesLabelled(lbls) => lbls.into_iter().map(Some).collect(), SomeNodesLabelled(lbls) => lbls.into_iter().collect(), } } diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index b24c208c76..c14165454e 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -9,6 +9,7 @@ doctest = false [dependencies] rustc_target = { path = "../rustc_target" } +rustc_feature = { path = "../rustc_feature" } rustc_macros = { path = "../rustc_macros" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 4ede9d67b7..a81eb747a3 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -5,6 +5,7 @@ use rustc_ast as ast; use rustc_ast::NodeId; use rustc_macros::HashStable_Generic; use rustc_span::hygiene::MacroKind; +use rustc_span::Symbol; use std::array::IntoIter; use std::fmt::Debug; @@ -34,7 +35,7 @@ pub enum CtorKind { #[derive(HashStable_Generic)] pub enum NonMacroAttrKind { /// Single-segment attribute defined by the language (`#[inline]`) - Builtin, + Builtin(Symbol), /// Multi-segment custom attribute living in a "tool module" (`#[rustfmt::skip]`). Tool, /// Single-segment custom attribute registered by a derive macro (`#[serde(default)]`). @@ -371,7 +372,7 @@ impl CtorKind { impl NonMacroAttrKind { pub fn descr(self) -> &'static str { match self { - NonMacroAttrKind::Builtin => "built-in attribute", + NonMacroAttrKind::Builtin(..) => "built-in attribute", NonMacroAttrKind::Tool => "tool attribute", NonMacroAttrKind::DeriveHelper | NonMacroAttrKind::DeriveHelperCompat => { "derive helper attribute" @@ -393,7 +394,7 @@ impl NonMacroAttrKind { NonMacroAttrKind::Tool | NonMacroAttrKind::DeriveHelper | NonMacroAttrKind::DeriveHelperCompat => true, - NonMacroAttrKind::Builtin | NonMacroAttrKind::Registered => false, + NonMacroAttrKind::Builtin(..) | NonMacroAttrKind::Registered => false, } } } diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index d5ade86593..6a1b9bdbb9 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -419,6 +419,10 @@ impl Definitions { pub fn add_parent_module_of_macro_def(&mut self, expn_id: ExpnId, module: DefId) { self.parent_modules_of_macro_defs.insert(expn_id, module); } + + pub fn iter_local_def_id(&self) -> impl Iterator + '_ { + self.def_id_to_hir_id.iter_enumerated().map(|(k, _)| k) + } } #[derive(Copy, Clone, PartialEq, Debug)] diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 2abebbd030..67a15418ea 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -11,9 +11,9 @@ pub use rustc_ast::{CaptureBy, Movability, Mutability}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_data_structures::sync::{par_for_each_in, Send, Sync}; use rustc_macros::HashStable_Generic; -use rustc_span::def_id::LocalDefId; -use rustc_span::source_map::Spanned; +use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::{def_id::LocalDefId, BytePos}; use rustc_span::{MultiSpan, Span, DUMMY_SP}; use rustc_target::asm::InlineAsmRegOrRegClass; use rustc_target::spec::abi::Abi; @@ -28,7 +28,7 @@ pub struct Lifetime { pub span: Span, /// Either "`'a`", referring to a named lifetime definition, - /// or "``" (i.e., `kw::Invalid`), for elision placeholders. + /// or "``" (i.e., `kw::Empty`), for elision placeholders. /// /// HIR lowering inserts these placeholders in type paths that /// refer to type definitions needing lifetime parameters, @@ -231,7 +231,11 @@ impl<'hir> PathSegment<'hir> { PathSegment { ident, hir_id: None, res: None, infer_args: true, args: None } } - pub fn generic_args(&self) -> &GenericArgs<'hir> { + pub fn invalid() -> Self { + Self::from_ident(Ident::invalid()) + } + + pub fn args(&self) -> &GenericArgs<'hir> { if let Some(ref args) = self.args { args } else { @@ -275,6 +279,10 @@ impl GenericArg<'_> { matches!(self, GenericArg::Const(_)) } + pub fn is_synthetic(&self) -> bool { + matches!(self, GenericArg::Lifetime(lifetime) if lifetime.name.ident() == Ident::invalid()) + } + pub fn descr(&self) -> &'static str { match self { GenericArg::Lifetime(_) => "lifetime", @@ -283,11 +291,11 @@ impl GenericArg<'_> { } } - pub fn short_descr(&self) -> &'static str { + pub fn to_ord(&self, feats: &rustc_feature::Features) -> ast::ParamKindOrd { match self { - GenericArg::Lifetime(_) => "lifetime", - GenericArg::Type(_) => "type", - GenericArg::Const(_) => "const", + GenericArg::Lifetime(_) => ast::ParamKindOrd::Lifetime, + GenericArg::Type(_) => ast::ParamKindOrd::Type, + GenericArg::Const(_) => ast::ParamKindOrd::Const { unordered: feats.const_generics }, } } } @@ -344,6 +352,39 @@ impl GenericArgs<'_> { own_counts } + + pub fn span(&self) -> Option { + self.args + .iter() + .filter(|arg| !arg.is_synthetic()) + .map(|arg| arg.span()) + .reduce(|span1, span2| span1.to(span2)) + } + + /// Returns span encompassing arguments and their surrounding `<>` or `()` + pub fn span_ext(&self, sm: &SourceMap) -> Option { + let mut span = self.span()?; + + let (o, c) = if self.parenthesized { ('(', ')') } else { ('<', '>') }; + + if let Ok(snippet) = sm.span_to_snippet(span) { + let snippet = snippet.as_bytes(); + + if snippet[0] != (o as u8) || snippet[snippet.len() - 1] != (c as u8) { + span = sm.span_extend_to_prev_char(span, o, true); + span = span.with_lo(span.lo() - BytePos(1)); + + span = sm.span_extend_to_next_char(span, c, true); + span = span.with_hi(span.hi() + BytePos(1)); + } + } + + Some(span) + } + + pub fn is_empty(&self) -> bool { + self.args.is_empty() + } } /// A modifier on a bound, currently this is only used for `?Sized`, where the @@ -378,9 +419,9 @@ impl GenericBound<'_> { pub fn span(&self) -> Span { match self { - &GenericBound::Trait(ref t, ..) => t.span, - &GenericBound::LangItemTrait(_, span, ..) => span, - &GenericBound::Outlives(ref l) => l.span, + GenericBound::Trait(t, ..) => t.span, + GenericBound::LangItemTrait(_, span, ..) => *span, + GenericBound::Outlives(l) => l.span, } } } @@ -418,6 +459,8 @@ pub enum GenericParamKind<'hir> { }, Const { ty: &'hir Ty<'hir>, + /// Optional default value for the const generic param + default: Option, }, } @@ -518,7 +561,7 @@ impl WhereClause<'_> { /// in `fn foo(t: T) where T: Foo,` so we don't suggest two trailing commas. pub fn tail_span_for_suggestion(&self) -> Span { let end = self.span_for_predicates_or_empty_place().shrink_to_hi(); - self.predicates.last().map(|p| p.span()).unwrap_or(end).shrink_to_hi().to(end) + self.predicates.last().map_or(end, |p| p.span()).shrink_to_hi().to(end) } } @@ -536,9 +579,9 @@ pub enum WherePredicate<'hir> { impl WherePredicate<'_> { pub fn span(&self) -> Span { match self { - &WherePredicate::BoundPredicate(ref p) => p.span, - &WherePredicate::RegionPredicate(ref p) => p.span, - &WherePredicate::EqPredicate(ref p) => p.span, + WherePredicate::BoundPredicate(p) => p.span, + WherePredicate::RegionPredicate(p) => p.span, + WherePredicate::EqPredicate(p) => p.span, } } } @@ -607,7 +650,7 @@ pub struct Crate<'hir> { // over the ids in increasing order. In principle it should not // matter what order we visit things in, but in *practice* it // does, because it can affect the order in which errors are - // detected, which in turn can make compile-fail tests yield + // detected, which in turn can make UI tests yield // slightly different results. pub items: BTreeMap>, @@ -760,9 +803,9 @@ pub struct Pat<'hir> { pub default_binding_modes: bool, } -impl Pat<'_> { +impl<'hir> Pat<'hir> { // FIXME(#19596) this is a workaround, but there should be a better way - fn walk_short_(&self, it: &mut impl FnMut(&Pat<'_>) -> bool) -> bool { + fn walk_short_(&self, it: &mut impl FnMut(&Pat<'hir>) -> bool) -> bool { if !it(self) { return false; } @@ -785,12 +828,12 @@ impl Pat<'_> { /// Note that when visiting e.g. `Tuple(ps)`, /// if visiting `ps[0]` returns `false`, /// then `ps[1]` will not be visited. - pub fn walk_short(&self, mut it: impl FnMut(&Pat<'_>) -> bool) -> bool { + pub fn walk_short(&self, mut it: impl FnMut(&Pat<'hir>) -> bool) -> bool { self.walk_short_(&mut it) } // FIXME(#19596) this is a workaround, but there should be a better way - fn walk_(&self, it: &mut impl FnMut(&Pat<'_>) -> bool) { + fn walk_(&self, it: &mut impl FnMut(&Pat<'hir>) -> bool) { if !it(self) { return; } @@ -810,7 +853,7 @@ impl Pat<'_> { /// Walk the pattern in left-to-right order. /// /// If `it(pat)` returns `false`, the children are not visited. - pub fn walk(&self, mut it: impl FnMut(&Pat<'_>) -> bool) { + pub fn walk(&self, mut it: impl FnMut(&Pat<'hir>) -> bool) { self.walk_(&mut it) } @@ -1388,6 +1431,7 @@ impl Expr<'_> { ExprKind::Lit(_) => ExprPrecedence::Lit, ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast, ExprKind::DropTemps(ref expr, ..) => expr.precedence(), + ExprKind::If(..) => ExprPrecedence::If, ExprKind::Loop(..) => ExprPrecedence::Loop, ExprKind::Match(..) => ExprPrecedence::Match, ExprKind::Closure(..) => ExprPrecedence::Closure, @@ -1449,6 +1493,7 @@ impl Expr<'_> { | ExprKind::MethodCall(..) | ExprKind::Struct(..) | ExprKind::Tup(..) + | ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Closure(..) | ExprKind::Block(..) @@ -1498,10 +1543,10 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool { **qpath, QPath::LangItem( LangItem::Range - | LangItem::RangeTo - | LangItem::RangeFrom - | LangItem::RangeFull - | LangItem::RangeToInclusive, + | LangItem::RangeTo + | LangItem::RangeFrom + | LangItem::RangeFull + | LangItem::RangeToInclusive, _, ) ), @@ -1565,10 +1610,16 @@ pub enum ExprKind<'hir> { /// This construct only exists to tweak the drop order in HIR lowering. /// An example of that is the desugaring of `for` loops. DropTemps(&'hir Expr<'hir>), + /// An `if` block, with an optional else block. + /// + /// I.e., `if { } else { }`. + If(&'hir Expr<'hir>, &'hir Expr<'hir>, Option<&'hir Expr<'hir>>), /// A conditionless loop (can be exited with `break`, `continue`, or `return`). /// /// I.e., `'label: loop { }`. - Loop(&'hir Block<'hir>, Option Trait for Foo { .. }`. - Impl { - unsafety: Unsafety, - polarity: ImplPolarity, - defaultness: Defaultness, - // We do not put a `Span` in `Defaultness` because it breaks foreign crate metadata - // decoding as `Span`s cannot be decoded when a `Session` is not available. - defaultness_span: Option, - constness: Constness, - generics: Generics<'hir>, - - /// The trait being implemented, if any. - of_trait: Option>, - - self_ty: &'hir Ty<'hir>, - items: &'hir [ImplItemRef<'hir>], - }, + Impl(Impl<'hir>), +} + +#[derive(Debug, HashStable_Generic)] +pub struct Impl<'hir> { + pub unsafety: Unsafety, + pub polarity: ImplPolarity, + pub defaultness: Defaultness, + // We do not put a `Span` in `Defaultness` because it breaks foreign crate metadata + // decoding as `Span`s cannot be decoded when a `Session` is not available. + pub defaultness_span: Option, + pub constness: Constness, + pub generics: Generics<'hir>, + + /// The trait being implemented, if any. + pub of_trait: Option>, + + pub self_ty: &'hir Ty<'hir>, + pub items: &'hir [ImplItemRef<'hir>], } impl ItemKind<'_> { @@ -2580,7 +2633,7 @@ impl ItemKind<'_> { | ItemKind::Struct(_, ref generics) | ItemKind::Union(_, ref generics) | ItemKind::Trait(_, _, ref generics, _, _) - | ItemKind::Impl { ref generics, .. } => generics, + | ItemKind::Impl(Impl { ref generics, .. }) => generics, _ => return None, }) } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 03c8b17388..f8b3f0d9b6 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -611,7 +611,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { // `visit_enum_def()` takes care of visiting the `Item`'s `HirId`. visitor.visit_enum_def(enum_definition, generics, item.hir_id, item.span) } - ItemKind::Impl { + ItemKind::Impl(Impl { unsafety: _, defaultness: _, polarity: _, @@ -621,7 +621,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { ref of_trait, ref self_ty, items, - } => { + }) => { visitor.visit_id(item.hir_id); visitor.visit_generics(generics); walk_list!(visitor, visit_trait_ref, of_trait); @@ -781,6 +781,7 @@ pub fn walk_assoc_type_binding<'v, V: Visitor<'v>>( ) { visitor.visit_id(type_binding.hir_id); visitor.visit_ident(type_binding.ident); + visitor.visit_generic_args(type_binding.span, type_binding.gen_args); match type_binding.kind { TypeBindingKind::Equality { ref ty } => { visitor.visit_ty(ty); @@ -877,7 +878,12 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Generi match param.kind { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { ref default, .. } => walk_list!(visitor, visit_ty, default), - GenericParamKind::Const { ref ty } => visitor.visit_ty(ty), + GenericParamKind::Const { ref ty, ref default } => { + visitor.visit_ty(ty); + if let Some(ref default) = default { + visitor.visit_anon_const(default); + } + } } walk_list!(visitor, visit_param_bound, param.bounds); } @@ -891,8 +897,8 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( visitor: &mut V, predicate: &'v WherePredicate<'v>, ) { - match predicate { - &WherePredicate::BoundPredicate(WhereBoundPredicate { + match *predicate { + WherePredicate::BoundPredicate(WhereBoundPredicate { ref bounded_ty, bounds, bound_generic_params, @@ -902,11 +908,11 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_generic_param, bound_generic_params); } - &WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, bounds, .. }) => { + WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, bounds, .. }) => { visitor.visit_lifetime(lifetime); walk_list!(visitor, visit_param_bound, bounds); } - &WherePredicate::EqPredicate(WhereEqPredicate { + WherePredicate::EqPredicate(WhereEqPredicate { hir_id, ref lhs_ty, ref rhs_ty, .. }) => { visitor.visit_id(hir_id); @@ -1141,7 +1147,12 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) ExprKind::DropTemps(ref subexpression) => { visitor.visit_expr(subexpression); } - ExprKind::Loop(ref block, ref opt_label, _) => { + ExprKind::If(ref cond, ref then, ref else_opt) => { + visitor.visit_expr(cond); + visitor.visit_expr(then); + walk_list!(visitor, visit_expr, else_opt); + } + ExprKind::Loop(ref block, ref opt_label, _, _) => { walk_list!(visitor, visit_label, opt_label); visitor.visit_block(block); } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 3e4eb9eafd..26ce30cb51 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -67,7 +67,7 @@ macro_rules! language_item_table { } } - #[derive(HashStable_Generic)] + #[derive(HashStable_Generic, Debug)] pub struct LanguageItems { /// Mappings from lang items to their possibly found `DefId`s. /// The index corresponds to the order in `LangItem`. @@ -157,7 +157,7 @@ where } language_item_table! { -// Variant name, Name, Method name, Target; +// Variant name, Name, Method name, Target; Bool, sym::bool, bool_impl, Target::Impl; Char, sym::char, char_impl, Target::Impl; Str, sym::str, str_impl, Target::Impl; diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 9d931b3a9e..c69a9b063a 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -2,7 +2,6 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html -#![feature(array_value_iter)] #![feature(crate_visibility_modifier)] #![feature(const_fn)] // For the unsizing cast on `&[]` #![feature(const_panic)] diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 2774cc9c08..6dbcfb963e 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -38,12 +38,14 @@ pub enum Target { Enum, Variant, Struct, + Field, Union, Trait, TraitAlias, Impl, Expression, Statement, + Arm, AssocConst, Method(MethodKind), AssocTy, @@ -51,6 +53,7 @@ pub enum Target { ForeignStatic, ForeignTy, GenericParam(GenericParamKind), + MacroDef, } impl Display for Target { @@ -73,12 +76,14 @@ impl Display for Target { Target::Enum => "enum", Target::Variant => "enum variant", Target::Struct => "struct", + Target::Field => "struct field", Target::Union => "union", Target::Trait => "trait", Target::TraitAlias => "trait alias", Target::Impl => "item", Target::Expression => "expression", Target::Statement => "statement", + Target::Arm => "match arm", Target::AssocConst => "associated const", Target::Method(_) => "method", Target::AssocTy => "associated type", @@ -90,6 +95,7 @@ impl Display for Target { GenericParamKind::Lifetime => "lifetime parameter", GenericParamKind::Const => "const parameter", }, + Target::MacroDef => "macro def", } ) } diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index 52f28bf8f4..b8cd15e7f0 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -4,7 +4,7 @@ use crate::def_id::DefId; use crate::{lang_items, LangItem, LanguageItems}; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_map::StableMap; use rustc_span::symbol::{sym, Symbol}; use std::lazy::SyncLazy; @@ -12,8 +12,8 @@ use std::lazy::SyncLazy; macro_rules! weak_lang_items { ($($name:ident, $item:ident, $sym:ident;)*) => ( -pub static WEAK_ITEMS_REFS: SyncLazy> = SyncLazy::new(|| { - let mut map = FxHashMap::default(); +pub static WEAK_ITEMS_REFS: SyncLazy> = SyncLazy::new(|| { + let mut map = StableMap::default(); $(map.insert(sym::$name, LangItem::$item);)* map }); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 0b5eb1d826..4595855309 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -138,9 +138,6 @@ impl std::ops::DerefMut for State<'_> { } impl<'a> PrintState<'a> for State<'a> { - fn insert_extra_parens(&self) -> bool { - true - } fn comments(&mut self) -> &mut Option> { &mut self.comments } @@ -687,7 +684,7 @@ impl<'a> State<'a> { self.head(visibility_qualified(&item.vis, "union")); self.print_struct(struct_def, generics, item.ident.name, item.span, true); } - hir::ItemKind::Impl { + hir::ItemKind::Impl(hir::Impl { unsafety, polarity, defaultness, @@ -697,7 +694,7 @@ impl<'a> State<'a> { ref of_trait, ref self_ty, items, - } => { + }) => { self.head(""); self.print_visibility(&item.vis); self.print_defaultness(defaultness); @@ -1083,6 +1080,50 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::Block(blk)) } + fn print_else(&mut self, els: Option<&hir::Expr<'_>>) { + match els { + Some(_else) => { + match _else.kind { + // "another else-if" + hir::ExprKind::If(ref i, ref then, ref e) => { + self.cbox(INDENT_UNIT - 1); + self.ibox(0); + self.s.word(" else if "); + self.print_expr_as_cond(&i); + self.s.space(); + self.print_expr(&then); + self.print_else(e.as_ref().map(|e| &**e)) + } + // "final else" + hir::ExprKind::Block(ref b, _) => { + self.cbox(INDENT_UNIT - 1); + self.ibox(0); + self.s.word(" else "); + self.print_block(&b) + } + // BLEAH, constraints would be great here + _ => { + panic!("print_if saw if with weird alternative"); + } + } + } + _ => {} + } + } + + pub fn print_if( + &mut self, + test: &hir::Expr<'_>, + blk: &hir::Expr<'_>, + elseopt: Option<&hir::Expr<'_>>, + ) { + self.head("if"); + self.print_expr_as_cond(test); + self.s.space(); + self.print_expr(blk); + self.print_else(elseopt) + } + pub fn print_anon_const(&mut self, constant: &hir::AnonConst) { self.ann.nested(self, Nested::Body(constant.body)) } @@ -1216,7 +1257,7 @@ impl<'a> State<'a> { self.s.word("."); self.print_ident(segment.ident); - let generic_args = segment.generic_args(); + let generic_args = segment.args(); if !generic_args.args.is_empty() || !generic_args.bindings.is_empty() { self.print_generic_args(generic_args, segment.infer_args, true); } @@ -1352,7 +1393,10 @@ impl<'a> State<'a> { // Print `}`: self.bclose_maybe_open(expr.span, true); } - hir::ExprKind::Loop(ref blk, opt_label, _) => { + hir::ExprKind::If(ref test, ref blk, ref elseopt) => { + self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e)); + } + hir::ExprKind::Loop(ref blk, opt_label, _, _) => { if let Some(label) = opt_label { self.print_ident(label.ident); self.word_space(":"); @@ -1664,11 +1708,7 @@ impl<'a> State<'a> { } if segment.ident.name != kw::PathRoot { self.print_ident(segment.ident); - self.print_generic_args( - segment.generic_args(), - segment.infer_args, - colons_before_params, - ); + self.print_generic_args(segment.args(), segment.infer_args, colons_before_params); } } } @@ -1676,7 +1716,7 @@ impl<'a> State<'a> { pub fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) { if segment.ident.name != kw::PathRoot { self.print_ident(segment.ident); - self.print_generic_args(segment.generic_args(), segment.infer_args, false); + self.print_generic_args(segment.args(), segment.infer_args, false); } } @@ -1696,7 +1736,7 @@ impl<'a> State<'a> { if segment.ident.name != kw::PathRoot { self.print_ident(segment.ident); self.print_generic_args( - segment.generic_args(), + segment.args(), segment.infer_args, colons_before_params, ); @@ -1708,7 +1748,7 @@ impl<'a> State<'a> { let item_segment = path.segments.last().unwrap(); self.print_ident(item_segment.ident); self.print_generic_args( - item_segment.generic_args(), + item_segment.args(), item_segment.infer_args, colons_before_params, ) @@ -1728,7 +1768,7 @@ impl<'a> State<'a> { self.s.word("::"); self.print_ident(item_segment.ident); self.print_generic_args( - item_segment.generic_args(), + item_segment.args(), item_segment.infer_args, colons_before_params, ) @@ -1800,6 +1840,7 @@ impl<'a> State<'a> { for binding in generic_args.bindings.iter() { start_or_comma(self); self.print_ident(binding.ident); + self.print_generic_args(binding.gen_args, false, false); self.s.space(); match generic_args.bindings[0].kind { hir::TypeBindingKind::Equality { ref ty } => { @@ -2205,9 +2246,12 @@ impl<'a> State<'a> { self.print_type(&default) } } - GenericParamKind::Const { ref ty } => { + GenericParamKind::Const { ref ty, ref default } => { self.word_space(":"); - self.print_type(ty) + self.print_type(ty); + if let Some(ref _default) = default { + // FIXME(const_generics_defaults): print the `default` value here + } } } } @@ -2230,19 +2274,19 @@ impl<'a> State<'a> { } match predicate { - &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - ref bound_generic_params, - ref bounded_ty, + hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + bound_generic_params, + bounded_ty, bounds, .. }) => { self.print_formal_generic_params(bound_generic_params); self.print_type(&bounded_ty); - self.print_bounds(":", bounds); + self.print_bounds(":", *bounds); } - &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { - ref lifetime, - ref bounds, + hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + lifetime, + bounds, .. }) => { self.print_lifetime(lifetime); @@ -2261,10 +2305,8 @@ impl<'a> State<'a> { } } } - &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { - ref lhs_ty, - ref rhs_ty, - .. + hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { + lhs_ty, rhs_ty, .. }) => { self.print_type(lhs_ty); self.s.space(); @@ -2435,7 +2477,13 @@ impl<'a> State<'a> { // // Duplicated from `parse::classify`, but adapted for the HIR. fn expr_requires_semi_to_be_stmt(e: &hir::Expr<'_>) -> bool { - !matches!(e.kind, hir::ExprKind::Match(..) | hir::ExprKind::Block(..) | hir::ExprKind::Loop(..)) + !matches!( + e.kind, + hir::ExprKind::If(..) + | hir::ExprKind::Match(..) + | hir::ExprKind::Block(..) + | hir::ExprKind::Loop(..) + ) } /// This statement requires a semicolon after it. diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index e17396422f..f39a92b9a3 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -12,7 +12,7 @@ //! In this code, we report errors on each `rustc_if_this_changed` //! annotation. If a path exists in all cases, then we would report //! "all path(s) exist". Otherwise, we report: "no path to `foo`" for -//! each case where no path exists. `compile-fail` tests can then be +//! each case where no path exists. `ui` tests can then be //! used to check when paths exist or do not. //! //! The full form of the `rustc_if_this_changed` annotation is @@ -310,13 +310,13 @@ fn filter_nodes<'q>( sources: &Option>, targets: &Option>, ) -> FxHashSet<&'q DepNode> { - if let &Some(ref sources) = sources { - if let &Some(ref targets) = targets { + if let Some(sources) = sources { + if let Some(targets) = targets { walk_between(query, sources, targets) } else { walk_nodes(query, sources, OUTGOING) } - } else if let &Some(ref targets) = targets { + } else if let Some(targets) = targets { walk_nodes(query, targets, INCOMING) } else { query.nodes().into_iter().collect() diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index a80c4be3e9..95456c07b1 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -17,7 +17,6 @@ mod persist; pub use assert_dep_graph::assert_dep_graph; pub use persist::copy_cgu_workproduct_to_incr_comp_cache_dir; pub use persist::delete_workproduct_files; -pub use persist::dep_graph_tcx_init; pub use persist::finalize_session_directory; pub use persist::garbage_collect_session_directories; pub use persist::in_incr_comp_dir; diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index e185ee24d1..087f83c247 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs @@ -14,7 +14,7 @@ use std::fs; use std::io::{self, Read}; use std::path::Path; -use rustc_serialize::opaque::Encoder; +use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; /// The first few bytes of files generated by incremental compilation. const FILE_MAGIC: &[u8] = b"RSIC"; @@ -27,15 +27,17 @@ const HEADER_FORMAT_VERSION: u16 = 0; /// the Git commit hash. const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION"); -pub fn write_file_header(stream: &mut Encoder, nightly_build: bool) { - stream.emit_raw_bytes(FILE_MAGIC); - stream - .emit_raw_bytes(&[(HEADER_FORMAT_VERSION >> 0) as u8, (HEADER_FORMAT_VERSION >> 8) as u8]); +pub fn write_file_header(stream: &mut FileEncoder, nightly_build: bool) -> FileEncodeResult { + stream.emit_raw_bytes(FILE_MAGIC)?; + stream.emit_raw_bytes(&[ + (HEADER_FORMAT_VERSION >> 0) as u8, + (HEADER_FORMAT_VERSION >> 8) as u8, + ])?; let rustc_version = rustc_version(nightly_build); assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize); - stream.emit_raw_bytes(&[rustc_version.len() as u8]); - stream.emit_raw_bytes(rustc_version.as_bytes()); + stream.emit_raw_bytes(&[rustc_version.len() as u8])?; + stream.emit_raw_bytes(rustc_version.as_bytes()) } /// Reads the contents of a file with a file header as defined in this module. @@ -52,11 +54,11 @@ pub fn read_file( path: &Path, nightly_build: bool, ) -> io::Result, usize)>> { - if !path.exists() { - return Ok(None); - } - - let data = fs::read(path)?; + let data = match fs::read(path) { + Ok(data) => data, + Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(None), + Err(err) => return Err(err), + }; let mut file = io::Cursor::new(data); diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 9fdf0a56d9..7a1976bed4 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -922,22 +922,24 @@ fn all_except_most_recent( /// before passing it to std::fs::remove_dir_all(). This will convert the path /// into the '\\?\' format, which supports much longer paths. fn safe_remove_dir_all(p: &Path) -> io::Result<()> { - if p.exists() { - let canonicalized = p.canonicalize()?; - std_fs::remove_dir_all(canonicalized) - } else { - Ok(()) - } + let canonicalized = match std_fs::canonicalize(p) { + Ok(canonicalized) => canonicalized, + Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), + Err(err) => return Err(err), + }; + + std_fs::remove_dir_all(canonicalized) } fn safe_remove_file(p: &Path) -> io::Result<()> { - if p.exists() { - let canonicalized = p.canonicalize()?; - match std_fs::remove_file(canonicalized) { - Err(ref err) if err.kind() == io::ErrorKind::NotFound => Ok(()), - result => result, - } - } else { - Ok(()) + let canonicalized = match std_fs::canonicalize(p) { + Ok(canonicalized) => canonicalized, + Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), + Err(err) => return Err(err), + }; + + match std_fs::remove_file(canonicalized) { + Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()), + result => result, } } diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 35428dc8d8..0add0c5aa2 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -4,7 +4,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::definitions::Definitions; use rustc_middle::dep_graph::{PreviousDepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; use rustc_middle::ty::query::OnDiskCache; -use rustc_middle::ty::TyCtxt; use rustc_serialize::opaque::Decoder; use rustc_serialize::Decodable as RustcDecodable; use rustc_session::Session; @@ -15,14 +14,6 @@ use super::file_format; use super::fs::*; use super::work_product; -pub fn dep_graph_tcx_init(tcx: TyCtxt<'_>) { - if !tcx.dep_graph.is_fully_enabled() { - return; - } - - tcx.allocate_metadata_dep_nodes(); -} - type WorkProductMap = FxHashMap; pub enum LoadResult { diff --git a/compiler/rustc_incremental/src/persist/mod.rs b/compiler/rustc_incremental/src/persist/mod.rs index 7bc3b47e15..8821b34b50 100644 --- a/compiler/rustc_incremental/src/persist/mod.rs +++ b/compiler/rustc_incremental/src/persist/mod.rs @@ -15,7 +15,6 @@ pub use fs::garbage_collect_session_directories; pub use fs::in_incr_comp_dir; pub use fs::in_incr_comp_dir_sess; pub use fs::prepare_session_directory; -pub use load::dep_graph_tcx_init; pub use load::load_query_result_cache; pub use load::LoadResult; pub use load::{load_dep_graph, DepGraphFuture}; diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 102a77e8e7..45d474b89b 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -1,11 +1,12 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::join; -use rustc_middle::dep_graph::{DepGraph, DepKind, WorkProduct, WorkProductId}; +use rustc_middle::dep_graph::{DepGraph, WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; -use rustc_serialize::opaque::Encoder; +use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_serialize::Encodable as RustcEncodable; use rustc_session::Session; use std::fs; +use std::io; use std::path::PathBuf; use super::data::*; @@ -32,12 +33,12 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { join( move || { sess.time("incr_comp_persist_result_cache", || { - save_in(sess, query_cache_path, |e| encode_query_cache(tcx, e)); + save_in(sess, query_cache_path, "query cache", |e| encode_query_cache(tcx, e)); }); }, || { sess.time("incr_comp_persist_dep_graph", || { - save_in(sess, dep_graph_path, |e| { + save_in(sess, dep_graph_path, "dependency graph", |e| { sess.time("incr_comp_encode_dep_graph", || encode_dep_graph(tcx, e)) }); }); @@ -64,7 +65,7 @@ pub fn save_work_product_index( debug!("save_work_product_index()"); dep_graph.assert_ignored(); let path = work_products_path(sess); - save_in(sess, path, |e| encode_work_product_index(&new_work_products, e)); + save_in(sess, path, "work product index", |e| encode_work_product_index(&new_work_products, e)); // We also need to clean out old work-products, as not all of them are // deleted during invalidation. Some object files don't change their @@ -91,138 +92,77 @@ pub fn save_work_product_index( }); } -fn save_in(sess: &Session, path_buf: PathBuf, encode: F) +fn save_in(sess: &Session, path_buf: PathBuf, name: &str, encode: F) where - F: FnOnce(&mut Encoder), + F: FnOnce(&mut FileEncoder) -> FileEncodeResult, { debug!("save: storing data in {}", path_buf.display()); - // delete the old dep-graph, if any + // Delete the old file, if any. // Note: It's important that we actually delete the old file and not just // truncate and overwrite it, since it might be a shared hard-link, the // underlying data of which we don't want to modify - if path_buf.exists() { - match fs::remove_file(&path_buf) { - Ok(()) => { - debug!("save: remove old file"); - } - Err(err) => { - sess.err(&format!( - "unable to delete old dep-graph at `{}`: {}", - path_buf.display(), - err - )); - return; - } - } - } - - // generate the data in a memory buffer - let mut encoder = Encoder::new(Vec::new()); - file_format::write_file_header(&mut encoder, sess.is_nightly_build()); - encode(&mut encoder); - - // write the data out - let data = encoder.into_inner(); - match fs::write(&path_buf, data) { - Ok(_) => { - debug!("save: data written to disk successfully"); + match fs::remove_file(&path_buf) { + Ok(()) => { + debug!("save: remove old file"); } + Err(err) if err.kind() == io::ErrorKind::NotFound => (), Err(err) => { - sess.err(&format!("failed to write dep-graph to `{}`: {}", path_buf.display(), err)); + sess.err(&format!( + "unable to delete old {} at `{}`: {}", + name, + path_buf.display(), + err + )); + return; } } -} - -fn encode_dep_graph(tcx: TyCtxt<'_>, encoder: &mut Encoder) { - // First encode the commandline arguments hash - tcx.sess.opts.dep_tracking_hash().encode(encoder).unwrap(); - // Encode the graph data. - let serialized_graph = - tcx.sess.time("incr_comp_serialize_dep_graph", || tcx.dep_graph.serialize()); - - if tcx.sess.opts.debugging_opts.incremental_info { - #[derive(Clone)] - struct Stat { - kind: DepKind, - node_counter: u64, - edge_counter: u64, + let mut encoder = match FileEncoder::new(&path_buf) { + Ok(encoder) => encoder, + Err(err) => { + sess.err(&format!("failed to create {} at `{}`: {}", name, path_buf.display(), err)); + return; } + }; - let total_node_count = serialized_graph.nodes.len(); - let total_edge_count = serialized_graph.edge_list_data.len(); - - let mut counts: FxHashMap<_, Stat> = - FxHashMap::with_capacity_and_hasher(total_node_count, Default::default()); + if let Err(err) = file_format::write_file_header(&mut encoder, sess.is_nightly_build()) { + sess.err(&format!("failed to write {} header to `{}`: {}", name, path_buf.display(), err)); + return; + } - for (i, &node) in serialized_graph.nodes.iter_enumerated() { - let stat = counts.entry(node.kind).or_insert(Stat { - kind: node.kind, - node_counter: 0, - edge_counter: 0, - }); + if let Err(err) = encode(&mut encoder) { + sess.err(&format!("failed to write {} to `{}`: {}", name, path_buf.display(), err)); + return; + } - stat.node_counter += 1; - let (edge_start, edge_end) = serialized_graph.edge_list_indices[i]; - stat.edge_counter += (edge_end - edge_start) as u64; - } + if let Err(err) = encoder.flush() { + sess.err(&format!("failed to flush {} to `{}`: {}", name, path_buf.display(), err)); + return; + } - let mut counts: Vec<_> = counts.values().cloned().collect(); - counts.sort_by_key(|s| -(s.node_counter as i64)); - - println!("[incremental]"); - println!("[incremental] DepGraph Statistics"); - - const SEPARATOR: &str = "[incremental] --------------------------------\ - ----------------------------------------------\ - ------------"; - - println!("{}", SEPARATOR); - println!("[incremental]"); - println!("[incremental] Total Node Count: {}", total_node_count); - println!("[incremental] Total Edge Count: {}", total_edge_count); - if let Some((total_edge_reads, total_duplicate_edge_reads)) = - tcx.dep_graph.edge_deduplication_data() - { - println!("[incremental] Total Edge Reads: {}", total_edge_reads); - println!("[incremental] Total Duplicate Edge Reads: {}", total_duplicate_edge_reads); - } - println!("[incremental]"); - println!( - "[incremental] {:<36}| {:<17}| {:<12}| {:<17}|", - "Node Kind", "Node Frequency", "Node Count", "Avg. Edge Count" - ); - println!( - "[incremental] -------------------------------------\ - |------------------\ - |-------------\ - |------------------|" - ); + debug!("save: data written to disk successfully"); +} - for stat in counts.iter() { - println!( - "[incremental] {:<36}|{:>16.1}% |{:>12} |{:>17.1} |", - format!("{:?}", stat.kind), - (100.0 * (stat.node_counter as f64)) / (total_node_count as f64), // percentage of all nodes - stat.node_counter, - (stat.edge_counter as f64) / (stat.node_counter as f64), // average edges per kind - ); - } +fn encode_dep_graph(tcx: TyCtxt<'_>, encoder: &mut FileEncoder) -> FileEncodeResult { + // First encode the commandline arguments hash + tcx.sess.opts.dep_tracking_hash().encode(encoder)?; - println!("{}", SEPARATOR); - println!("[incremental]"); + if tcx.sess.opts.debugging_opts.incremental_info { + tcx.dep_graph.print_incremental_info(); } - tcx.sess.time("incr_comp_encode_serialized_dep_graph", || { - serialized_graph.encode(encoder).unwrap(); - }); + // There is a tiny window between printing the incremental info above and encoding the dep + // graph below in which the dep graph could change, thus making the printed incremental info + // slightly out of date. If this matters to you, please feel free to submit a patch. :) + + tcx.sess.time("incr_comp_encode_serialized_dep_graph", || tcx.dep_graph.encode(encoder)) } fn encode_work_product_index( work_products: &FxHashMap, - encoder: &mut Encoder, -) { + encoder: &mut FileEncoder, +) -> FileEncodeResult { let serialized_products: Vec<_> = work_products .iter() .map(|(id, work_product)| SerializedWorkProduct { @@ -231,11 +171,9 @@ fn encode_work_product_index( }) .collect(); - serialized_products.encode(encoder).unwrap(); + serialized_products.encode(encoder) } -fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut Encoder) { - tcx.sess.time("incr_comp_serialize_result_cache", || { - tcx.serialize_query_result_cache(encoder).unwrap(); - }) +fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut FileEncoder) -> FileEncodeResult { + tcx.sess.time("incr_comp_serialize_result_cache", || tcx.serialize_query_result_cache(encoder)) } diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 0b501da7cd..100824f4b9 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -707,6 +707,18 @@ impl GrowableBitSet { self.bit_set.insert(elem) } + /// Returns `true` if the set has changed. + #[inline] + pub fn remove(&mut self, elem: T) -> bool { + self.ensure(elem.index() + 1); + self.bit_set.remove(elem) + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.bit_set.is_empty() + } + #[inline] pub fn contains(&self, elem: T) -> bool { let (word_index, mask) = word_index_and_mask(elem); diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index 6cc3e9427d..c11b98e77a 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs @@ -1,6 +1,7 @@ use super::*; extern crate test; +use std::hint::black_box; use test::Bencher; #[test] @@ -364,3 +365,36 @@ fn union_hybrid_sparse_full_small_domain(b: &mut Bencher) { sparse.union(&dense); }) } + +#[bench] +fn bench_insert(b: &mut Bencher) { + let mut bs = BitSet::new_filled(99999usize); + b.iter(|| { + black_box(bs.insert(black_box(100u32))); + }); +} + +#[bench] +fn bench_remove(b: &mut Bencher) { + let mut bs = BitSet::new_filled(99999usize); + b.iter(|| { + black_box(bs.remove(black_box(100u32))); + }); +} + +#[bench] +fn bench_iter(b: &mut Bencher) { + let bs = BitSet::new_filled(99999usize); + b.iter(|| { + bs.iter().map(|b: usize| black_box(b)).for_each(drop); + }); +} + +#[bench] +fn bench_intersect(b: &mut Bencher) { + let mut ba: BitSet = BitSet::new_filled(99999usize); + let bb = BitSet::new_filled(99999usize); + b.iter(|| { + ba.intersect(black_box(&bb)); + }); +} diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 9002d251f1..aa4fd055d5 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -353,10 +353,8 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { // `TyVar(vid)` is unresolved, track its universe index in the canonicalized // result. Err(mut ui) => { - if !self.infcx.unwrap().tcx.sess.opts.debugging_opts.chalk { - // FIXME: perf problem described in #55921. - ui = ty::UniverseIndex::ROOT; - } + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; self.canonicalize_ty_var( CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), @@ -440,10 +438,8 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { // `ConstVar(vid)` is unresolved, track its universe index in the // canonicalized result Err(mut ui) => { - if !self.infcx.unwrap().tcx.sess.opts.debugging_opts.chalk { - // FIXME: perf problem described in #55921. - ui = ty::UniverseIndex::ROOT; - } + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; return self.canonicalize_const_var( CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) }, ct, diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 71ce50f745..1546c1e559 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -14,7 +14,7 @@ use crate::infer::canonical::{ }; use crate::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; -use crate::infer::{InferCtxt, InferOk, InferResult, NLLRegionVariableOrigin}; +use crate::infer::{InferCtxt, InferOk, InferResult, NllRegionVariableOrigin}; use crate::traits::query::{Fallible, NoSolution}; use crate::traits::TraitEngine; use crate::traits::{Obligation, ObligationCause, PredicateObligation}; @@ -530,10 +530,10 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let atom = match k1.unpack() { GenericArgKind::Lifetime(r1) => { - ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r1, r2)) + ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r1, r2)) } GenericArgKind::Type(t1) => { - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(t1, r2)) + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(t1, r2)) } GenericArgKind::Const(..) => { // Consts cannot outlive one another, so we don't expect to @@ -541,8 +541,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { span_bug!(cause.span, "unexpected const outlives {:?}", constraint); } }; - let predicate = - predicate.rebind(atom).potentially_quantified(self.tcx, ty::PredicateKind::ForAll); + let predicate = predicate.rebind(atom).to_predicate(self.tcx); Obligation::new(cause.clone(), param_env, predicate) }) @@ -645,7 +644,7 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { } fn next_existential_region_var(&mut self, from_forall: bool) -> ty::Region<'tcx> { - let origin = NLLRegionVariableOrigin::Existential { from_forall }; + let origin = NllRegionVariableOrigin::Existential { from_forall }; self.infcx.next_nll_region_var(origin) } @@ -655,7 +654,7 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { self.infcx.next_nll_region_var_in_universe( - NLLRegionVariableOrigin::Existential { from_forall: false }, + NllRegionVariableOrigin::Existential { from_forall: false }, universe, ) } @@ -664,7 +663,7 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { self.obligations.push(Obligation { cause: self.cause.clone(), param_env: self.param_env, - predicate: ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(sup, sub)) + predicate: ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(sup, sub)) .to_predicate(self.infcx.tcx), recursion_depth: 0, }); diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index e38eebe23b..e034ac5e8f 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -34,7 +34,6 @@ use super::{InferCtxt, MiscVariable, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; -use rustc_ast as ast; use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; use rustc_middle::traits::ObligationCause; @@ -281,7 +280,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { &self, vid_is_expected: bool, vid: ty::FloatVid, - val: ast::FloatTy, + val: ty::FloatTy, ) -> RelateResult<'tcx, Ty<'tcx>> { self.inner .borrow_mut() @@ -358,7 +357,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { self.obligations.push(Obligation::new( self.trace.cause.clone(), self.param_env, - ty::PredicateAtom::WellFormed(b_ty.into()).to_predicate(self.infcx.tcx), + ty::PredicateKind::WellFormed(b_ty.into()).to_predicate(self.infcx.tcx), )); } @@ -451,9 +450,9 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { b: &'tcx ty::Const<'tcx>, ) { let predicate = if a_is_expected { - ty::PredicateAtom::ConstEquate(a, b) + ty::PredicateKind::ConstEquate(a, b) } else { - ty::PredicateAtom::ConstEquate(b, a) + ty::PredicateKind::ConstEquate(b, a) }; self.obligations.push(Obligation::new( self.trace.cause.clone(), diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 6b7edde9a6..84aa19aede 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -69,7 +69,7 @@ use rustc_middle::ty::{ subst::{Subst, SubstsRef}, Region, Ty, TyCtxt, TypeFoldable, }; -use rustc_span::{BytePos, DesugaringKind, Pos, Span}; +use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; use std::ops::ControlFlow; use std::{cmp, fmt}; @@ -98,7 +98,7 @@ pub(super) fn note_and_explain_region( // uh oh, hope no user ever sees THIS ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), None), - ty::RePlaceholder(_) => ("any other region".to_string(), None), + ty::RePlaceholder(_) => return, // FIXME(#13998) RePlaceholder should probably print like // ReFree rather than dumping Debug output on the user. @@ -153,6 +153,7 @@ fn msg_span_from_early_bound_and_free_regions( Some(Node::Item(it)) => item_scope_tag(&it), Some(Node::TraitItem(it)) => trait_item_scope_tag(&it), Some(Node::ImplItem(it)) => impl_item_scope_tag(&it), + Some(Node::ForeignItem(it)) => foreign_item_scope_tag(&it), _ => unreachable!(), }; let (prefix, span) = match *region { @@ -233,6 +234,13 @@ fn impl_item_scope_tag(item: &hir::ImplItem<'_>) -> &'static str { } } +fn foreign_item_scope_tag(item: &hir::ForeignItem<'_>) -> &'static str { + match item.kind { + hir::ForeignItemKind::Fn(..) => "method body", + hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => "associated item", + } +} + fn explain_span(tcx: TyCtxt<'tcx>, heading: &str, span: Span) -> (String, Option) { let lo = tcx.sess.source_map().lookup_char_pos(span.lo()); (format!("the {} at {}:{}", heading, lo.line, lo.col.to_usize() + 1), Some(span)) @@ -417,7 +425,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // obviously it never weeds out ALL errors. fn process_errors( &self, - errors: &Vec>, + errors: &[RegionResolutionError<'tcx>], ) -> Vec> { debug!("process_errors()"); @@ -442,7 +450,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { - errors.clone() + errors.to_owned() } else { errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect() }; @@ -907,7 +915,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); return Some(()); } - if let &ty::Adt(def, _) = ta.kind() { + if let ty::Adt(def, _) = ta.kind() { let path_ = self.tcx.def_path_str(def.did); if path_ == other_path { self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); @@ -950,7 +958,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ty::GenericParamDefKind::Type { has_default, .. } => { Some((param.def_id, has_default)) } - ty::GenericParamDefKind::Const => None, // FIXME(const_generics:defaults) + ty::GenericParamDefKind::Const => None, // FIXME(const_generics_defaults) }) .peekable(); let has_default = { @@ -1653,6 +1661,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { debug!("exp_found {:?} terr {:?}", exp_found, terr); if let Some(exp_found) = exp_found { self.suggest_as_ref_where_appropriate(span, &exp_found, diag); + self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); } @@ -1667,6 +1676,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.check_and_note_conflicting_crates(diag, terr); self.tcx.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id.to_def_id()); + if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values { + if let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() { + if let Some(def_id) = def_id.as_local() { + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + let span = self.tcx.hir().span(hir_id); + diag.span_note(span, "this closure does not fulfill the lifetime requirements"); + } + } + } + // It reads better to have the error origin as the final // thing. self.note_error_origin(diag, cause, exp_found); @@ -1688,8 +1707,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { for (predicate, _) in bounds { let predicate = predicate.subst(self.tcx, substs); - if let ty::PredicateAtom::Projection(projection_predicate) = - predicate.skip_binders() + if let ty::PredicateKind::Projection(projection_predicate) = + predicate.kind().skip_binder() { if projection_predicate.projection_ty.item_def_id == item_def_id { // We don't account for multiple `Future::Output = Ty` contraints. @@ -1801,6 +1820,53 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn suggest_accessing_field_where_appropriate( + &self, + cause: &ObligationCause<'tcx>, + exp_found: &ty::error::ExpectedFound>, + diag: &mut DiagnosticBuilder<'tcx>, + ) { + debug!( + "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})", + cause, exp_found + ); + if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() { + if expected_def.is_enum() { + return; + } + + if let Some((name, ty)) = expected_def + .non_enum_variant() + .fields + .iter() + .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) + .map(|field| (field.ident.name, field.ty(self.tcx, expected_substs))) + .find(|(_, ty)| ty::TyS::same_type(ty, exp_found.found)) + { + if let ObligationCauseCode::Pattern { span: Some(span), .. } = cause.code { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let suggestion = if expected_def.is_struct() { + format!("{}.{}", snippet, name) + } else if expected_def.is_union() { + format!("unsafe {{ {}.{} }}", snippet, name) + } else { + return; + }; + diag.span_suggestion( + span, + &format!( + "you might have meant to use field `{}` whose type is `{}`", + name, ty + ), + suggestion, + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate, /// suggests it. fn suggest_as_ref_where_appropriate( @@ -2100,7 +2166,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let consider = format!( "{} {}...", msg, - if type_param_span.map(|(_, _, is_impl_trait)| is_impl_trait).unwrap_or(false) { + if type_param_span.map_or(false, |(_, _, is_impl_trait)| is_impl_trait) { format!(" `{}` to `{}`", sub, bound_kind) } else { format!("`{}: {}`", bound_kind, sub) @@ -2274,6 +2340,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.note_region_origin(&mut err, &sub_origin); err.emit(); } + + /// Determine whether an error associated with the given span and definition + /// should be treated as being caused by the implicit `From` conversion + /// within `?` desugaring. + pub fn is_try_conversion(&self, span: Span, trait_def_id: DefId) -> bool { + span.is_desugaring(DesugaringKind::QuestionMark) + && self.tcx.is_diagnostic_item(sym::from_trait, trait_def_id) + } } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { @@ -2316,7 +2390,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); format!(" for capture of `{}` by closure", var_name) } - infer::NLL(..) => bug!("NLL variable found in lexical phase"), + infer::Nll(..) => bug!("NLL variable found in lexical phase"), }; struct_span_err!( diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 373f0a602c..bd43d3c01e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -3,13 +3,14 @@ use crate::infer::InferCtxt; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat}; use rustc_middle::hir::map::Map; use rustc_middle::infer::unify_key::ConstVariableOriginKind; use rustc_middle::ty::print::Print; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, DefIdTree, InferConst, Ty}; +use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt}; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::kw; use rustc_span::Span; @@ -25,6 +26,7 @@ struct FindHirNodeVisitor<'a, 'tcx> { found_closure: Option<&'tcx Expr<'tcx>>, found_method_call: Option<&'tcx Expr<'tcx>>, found_exact_method_call: Option<&'tcx Expr<'tcx>>, + found_use_diagnostic: Option>, } impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { @@ -39,44 +41,43 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { found_closure: None, found_method_call: None, found_exact_method_call: None, + found_use_diagnostic: None, } } - fn node_ty_contains_target(&mut self, hir_id: HirId) -> Option> { - let ty_opt = self - .infcx - .in_progress_typeck_results - .and_then(|typeck_results| typeck_results.borrow().node_type_opt(hir_id)); - match ty_opt { - Some(ty) => { - let ty = self.infcx.resolve_vars_if_possible(ty); - if ty.walk().any(|inner| { - inner == self.target - || match (inner.unpack(), self.target.unpack()) { - (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { - match (inner_ty.kind(), target_ty.kind()) { - ( - &ty::Infer(ty::TyVar(a_vid)), - &ty::Infer(ty::TyVar(b_vid)), - ) => self - .infcx - .inner - .borrow_mut() - .type_variables() - .sub_unified(a_vid, b_vid), - _ => false, - } + fn node_type_opt(&self, hir_id: HirId) -> Option> { + self.infcx.in_progress_typeck_results?.borrow().node_type_opt(hir_id) + } + + fn node_ty_contains_target(&self, hir_id: HirId) -> Option> { + self.node_type_opt(hir_id).map(|ty| self.infcx.resolve_vars_if_possible(ty)).filter(|ty| { + ty.walk().any(|inner| { + inner == self.target + || match (inner.unpack(), self.target.unpack()) { + (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { + use ty::{Infer, TyVar}; + match (inner_ty.kind(), target_ty.kind()) { + (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => self + .infcx + .inner + .borrow_mut() + .type_variables() + .sub_unified(a_vid, b_vid), + _ => false, } - _ => false, } - }) { - Some(ty) - } else { - None - } - } - None => None, - } + _ => false, + } + }) + }) + } + + /// Determine whether the expression, assumed to be the callee within a `Call`, + /// corresponds to the `From::from` emitted in desugaring of the `?` operator. + fn is_try_conversion(&self, callee: &Expr<'tcx>) -> bool { + self.infcx + .trait_def_from_hir_fn(callee.hir_id) + .map_or(false, |def_id| self.infcx.is_try_conversion(callee.span, def_id)) } } @@ -129,10 +130,23 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> { // are handled specially, but instead they should be handled in `annotate_method_call`, // which currently doesn't work because this evaluates to `false` for const arguments. // See https://github.com/rust-lang/rust/pull/77758 for more details. - if self.node_ty_contains_target(expr.hir_id).is_some() { + if let Some(ty) = self.node_ty_contains_target(expr.hir_id) { match expr.kind { ExprKind::Closure(..) => self.found_closure = Some(&expr), ExprKind::MethodCall(..) => self.found_method_call = Some(&expr), + + // If the given expression falls within the target span and is a + // `From::from(e)` call emitted during desugaring of the `?` operator, + // extract the types inferred before and after the call + ExprKind::Call(callee, [arg]) + if self.target_span.contains(expr.span) + && self.found_use_diagnostic.is_none() + && self.is_try_conversion(callee) => + { + self.found_use_diagnostic = self.node_type_opt(arg.hir_id).map(|pre_ty| { + UseDiagnostic::TryConversion { pre_ty, post_ty: ty, span: callee.span } + }); + } _ => {} } } @@ -140,17 +154,70 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> { } } +/// An observation about the use site of a type to be emitted as an additional +/// note in an inference failure error. +enum UseDiagnostic<'tcx> { + /// Records the types inferred before and after `From::from` is called on the + /// error value within the desugaring of the `?` operator. + TryConversion { pre_ty: Ty<'tcx>, post_ty: Ty<'tcx>, span: Span }, +} + +impl UseDiagnostic<'_> { + /// Return a descriptor of the value at the use site + fn descr(&self) -> &'static str { + match self { + Self::TryConversion { .. } => "error for `?` operator", + } + } + + /// Return a descriptor of the type at the use site + fn type_descr(&self) -> &'static str { + match self { + Self::TryConversion { .. } => "error type for `?` operator", + } + } + + fn applies_to(&self, span: Span) -> bool { + match *self { + // In some cases the span for an inference failure due to try + // conversion contains the antecedent expression as well as the `?` + Self::TryConversion { span: s, .. } => span.contains(s) && span.hi() == s.hi(), + } + } + + fn attach_note(&self, err: &mut DiagnosticBuilder<'_>) { + match *self { + Self::TryConversion { pre_ty, post_ty, .. } => { + let intro = "`?` implicitly converts the error value"; + + let msg = match (pre_ty.is_ty_infer(), post_ty.is_ty_infer()) { + (true, true) => format!("{} using the `From` trait", intro), + (false, true) => { + format!("{} into a type implementing `From<{}>`", intro, pre_ty) + } + (true, false) => { + format!("{} into `{}` using the `From` trait", intro, post_ty) + } + (false, false) => { + format!( + "{} into `{}` using its implementation of `From<{}>`", + intro, post_ty, pre_ty + ) + } + }; + + err.note(&msg); + } + } + } +} + /// Suggest giving an appropriate return type to a closure expression. fn closure_return_type_suggestion( - span: Span, err: &mut DiagnosticBuilder<'_>, output: &FnRetTy<'_>, body: &Body<'_>, - descr: &str, - name: &str, ret: &str, - parent_name: Option, - parent_descr: Option<&str>, ) { let (arrow, post) = match output { FnRetTy::DefaultReturn(_) => ("-> ", " "), @@ -168,10 +235,6 @@ fn closure_return_type_suggestion( suggestion, Applicability::HasPlaceholders, ); - err.span_label( - span, - InferCtxt::cannot_infer_msg("type", &name, &descr, parent_name, parent_descr), - ); } /// Given a closure signature, return a `String` containing a list of all its argument types. @@ -216,9 +279,67 @@ impl Into for TypeAnnotationNeeded { pub struct InferenceDiagnosticsData { pub name: String, pub span: Option, - pub description: Cow<'static, str>, - pub parent_name: Option, - pub parent_description: Option<&'static str>, + pub kind: UnderspecifiedArgKind, + pub parent: Option, +} + +/// Data on the parent definition where a generic argument was declared. +pub struct InferenceDiagnosticsParentData { + pub prefix: &'static str, + pub name: String, +} + +pub enum UnderspecifiedArgKind { + Type { prefix: Cow<'static, str> }, + Const { is_parameter: bool }, +} + +impl InferenceDiagnosticsData { + /// Generate a label for a generic argument which can't be inferred. When not + /// much is known about the argument, `use_diag` may be used to describe the + /// labeled value. + fn cannot_infer_msg(&self, use_diag: Option<&UseDiagnostic<'_>>) -> String { + if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) { + if let Some(use_diag) = use_diag { + return format!("cannot infer type of {}", use_diag.descr()); + } + + return "cannot infer type".to_string(); + } + + let suffix = match (&self.parent, use_diag) { + (Some(parent), _) => format!(" declared on the {} `{}`", parent.prefix, parent.name), + (None, Some(use_diag)) => format!(" in {}", use_diag.type_descr()), + (None, None) => String::new(), + }; + + // For example: "cannot infer type for type parameter `T`" + format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix) + } +} + +impl InferenceDiagnosticsParentData { + fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + let parent_def_id = tcx.parent(def_id)?; + + let parent_name = + tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string(); + + Some(InferenceDiagnosticsParentData { + prefix: tcx.def_kind(parent_def_id).descr(parent_def_id), + name: parent_name, + }) + } +} + +impl UnderspecifiedArgKind { + fn prefix_string(&self) -> Cow<'static, str> { + match self { + Self::Type { prefix } => format!("type for {}", prefix).into(), + Self::Const { is_parameter: true } => "the value of const parameter".into(), + Self::Const { is_parameter: false } => "the value of the constant".into(), + } + } } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { @@ -238,32 +359,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) = var_origin.kind { - let parent_def_id = def_id.and_then(|def_id| self.tcx.parent(def_id)); - let (parent_name, parent_description) = - if let Some(parent_def_id) = parent_def_id { - let parent_name = self - .tcx - .def_key(parent_def_id) - .disambiguated_data - .data - .get_opt_name() - .map(|parent_symbol| parent_symbol.to_string()); - - ( - parent_name, - Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)), - ) - } else { - (None, None) - }; - if name != kw::SelfUpper { return InferenceDiagnosticsData { name: name.to_string(), span: Some(var_origin.span), - description: "type parameter".into(), - parent_name, - parent_description, + kind: UnderspecifiedArgKind::Type { + prefix: "type parameter".into(), + }, + parent: def_id.and_then(|def_id| { + InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id) + }), }; } } @@ -278,9 +383,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { InferenceDiagnosticsData { name: s, span: None, - description: ty.prefix_string(), - parent_name: None, - parent_description: None, + kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string() }, + parent: None, } } GenericArgKind::Const(ct) => { @@ -290,31 +394,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) = origin.kind { - let parent_def_id = self.tcx.parent(def_id); - let (parent_name, parent_description) = - if let Some(parent_def_id) = parent_def_id { - let parent_name = self - .tcx - .def_key(parent_def_id) - .disambiguated_data - .data - .get_opt_name() - .map(|parent_symbol| parent_symbol.to_string()); - - ( - parent_name, - Some(self.tcx.def_kind(parent_def_id).descr(parent_def_id)), - ) - } else { - (None, None) - }; - return InferenceDiagnosticsData { name: name.to_string(), span: Some(origin.span), - description: "const parameter".into(), - parent_name, - parent_description, + kind: UnderspecifiedArgKind::Const { is_parameter: true }, + parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id), }; } @@ -329,9 +413,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { InferenceDiagnosticsData { name: s, span: Some(origin.span), - description: "the constant".into(), - parent_name: None, - parent_description: None, + kind: UnderspecifiedArgKind::Const { is_parameter: false }, + parent: None, } } else { bug!("unexpect const: {:?}", ct); @@ -346,6 +429,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { body_id: Option, span: Span, arg: GenericArg<'tcx>, + impl_candidates: Vec>, error_code: TypeAnnotationNeeded, ) -> DiagnosticBuilder<'tcx> { let arg = self.resolve_vars_if_possible(arg); @@ -430,7 +514,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // When `arg_data.name` corresponds to a type argument, show the path of the full type we're // trying to infer. In the following example, `ty_msg` contains - // " in `std::result::Result`": + // " for `std::result::Result`": // ``` // error[E0282]: type annotations needed for `std::result::Result` // --> file.rs:L:CC @@ -448,6 +532,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { error_code, ); + let use_diag = local_visitor.found_use_diagnostic.as_ref(); + if let Some(use_diag) = use_diag { + if use_diag.applies_to(err_span) { + use_diag.attach_note(&mut err); + } + } + let suffix = match local_visitor.found_node_ty { Some(ty) if ty.is_closure() => { let substs = @@ -463,18 +554,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some((decl, body_id)) = closure_decl_and_body_id { closure_return_type_suggestion( - span, &mut err, &decl.output, self.tcx.hir().body(body_id), - &arg_data.description, - &arg_data.name, &ret, - arg_data.parent_name, - arg_data.parent_description, ); // We don't want to give the other suggestions when the problem is the // closure return type. + err.span_label( + span, + arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))), + ); return err; } @@ -564,7 +654,44 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; err.span_label(pattern.span, msg); } else if let Some(e) = local_visitor.found_method_call { - if let ExprKind::MethodCall(segment, ..) = &e.kind { + if let ExprKind::MethodCall(segment, _, exprs, _) = &e.kind { + // Suggest impl candidates: + // + // error[E0283]: type annotations needed + // --> $DIR/E0283.rs:35:24 + // | + // LL | let bar = foo_impl.into() * 1u32; + // | ---------^^^^-- + // | | | + // | | cannot infer type for type parameter `T` declared on the trait `Into` + // | this method call resolves to `T` + // | help: specify type like: `>::into(foo_impl)` + // | + // = note: cannot satisfy `Impl: Into<_>` + if !impl_candidates.is_empty() && e.span.contains(span) { + if let Some(expr) = exprs.first() { + if let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind { + if let [path_segment] = &path.segments[..] { + let candidate_len = impl_candidates.len(); + let suggestions = impl_candidates.iter().map(|candidate| { + format!( + "{}::{}({})", + candidate, segment.ident, path_segment.ident + ) + }); + err.span_suggestions( + e.span, + &format!( + "use the fully qualified path for the potential candidate{}", + pluralize!(candidate_len), + ), + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + }; + } // Suggest specifying type params or point out the return type of the call: // // error[E0282]: type annotations needed @@ -611,6 +738,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // | // = note: type must be known at this point let span = arg_data.span.unwrap_or(err_span); + + // Avoid multiple labels pointing at `span`. if !err .span .span_labels() @@ -618,44 +747,44 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .any(|span_label| span_label.label.is_some() && span_label.span == span) && local_visitor.found_arg_pattern.is_none() { - let (kind_str, const_value) = match arg.unpack() { - GenericArgKind::Type(_) => ("type", None), - GenericArgKind::Const(_) => ("the value", Some(())), - GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), - }; - // FIXME(const_generics): we would like to handle const arguments // as part of the normal diagnostics flow below, but there appear to // be subtleties in doing so, so for now we special-case const args // here. - if let Some(suggestion) = const_value - .and_then(|_| arg_data.parent_name.as_ref()) - .map(|parent| format!("{}::<{}>", parent, arg_data.name)) + if let (UnderspecifiedArgKind::Const { .. }, Some(parent_data)) = + (&arg_data.kind, &arg_data.parent) { err.span_suggestion_verbose( span, "consider specifying the const argument", - suggestion, + format!("{}::<{}>", parent_data.name, arg_data.name), Applicability::MaybeIncorrect, ); } - // Avoid multiple labels pointing at `span`. err.span_label( span, - InferCtxt::cannot_infer_msg( - kind_str, - &arg_data.name, - &arg_data.description, - arg_data.parent_name, - arg_data.parent_description, - ), + arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))), ); } err } + fn trait_def_from_hir_fn(&self, hir_id: hir::HirId) -> Option { + // The DefId will be the method's trait item ID unless this is an inherent impl + if let Some((DefKind::AssocFn, def_id)) = + self.in_progress_typeck_results?.borrow().type_dependent_def(hir_id) + { + return self + .tcx + .parent(def_id) + .filter(|&parent_def_id| self.tcx.is_trait(parent_def_id)); + } + + None + } + /// If the `FnSig` for the method call can be found and type arguments are identified as /// needed, suggest annotating the call, otherwise point out the resulting type of the call. fn annotate_method_call( @@ -718,49 +847,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "type inside {} must be known in this context", kind, ); - err.span_label( - span, - InferCtxt::cannot_infer_msg( - "type", - &data.name, - &data.description, - data.parent_name, - data.parent_description, - ), - ); + err.span_label(span, data.cannot_infer_msg(None)); err } - - fn cannot_infer_msg( - kind_str: &str, - type_name: &str, - descr: &str, - parent_name: Option, - parent_descr: Option<&str>, - ) -> String { - if type_name == "_" { - format!("cannot infer {}", kind_str) - } else { - let parent_desc = if let Some(parent_name) = parent_name { - let parent_type_descr = if let Some(parent_descr) = parent_descr { - format!(" the {}", parent_descr) - } else { - "".into() - }; - - format!(" declared on{} `{}`", parent_type_descr, parent_name) - } else { - "".to_string() - }; - - // FIXME: We really shouldn't be dealing with strings here - // but instead use a sensible enum for cases like this. - let preposition = if "the value" == kind_str { "of" } else { "for" }; - // For example: "cannot infer type for type parameter `T`" - format!( - "cannot infer {} {} {} `{}`{}", - kind_str, preposition, descr, type_name, parent_desc - ) - } - } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 5264854d8e..c6ae71ba33 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -345,9 +345,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { match tcx.hir().get_if_local(def_id) { Some(Node::ImplItem(ImplItem { ident, hir_id, .. })) => { match tcx.hir().find(tcx.hir().get_parent_item(*hir_id)) { - Some(Node::Item(Item { kind: ItemKind::Impl { self_ty, .. }, .. })) => { - Some((*ident, self_ty)) - } + Some(Node::Item(Item { + kind: ItemKind::Impl(hir::Impl { self_ty, .. }), + .. + })) => Some((*ident, self_ty)), _ => None, } } @@ -367,7 +368,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let impl_did = tcx.hir().local_def_id(*impl_node); match tcx.hir().get_if_local(impl_did.to_def_id()) { Some(Node::Item(Item { - kind: ItemKind::Impl { self_ty, .. }, + kind: ItemKind::Impl(hir::Impl { self_ty, .. }), .. })) if trait_objects.iter().all(|did| { // FIXME: we should check `self_ty` against the receiver diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index 4d3217a9c0..61c8113d05 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -132,9 +132,11 @@ impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { [segment] if segment .res - .map(|res| match res { - Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _) => true, - _ => false, + .map(|res| { + matches!( + res, + Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _) + ) }) .unwrap_or(false) => { diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 7fb94332ca..c88869abc2 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -1,6 +1,7 @@ use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt}; use crate::infer::{self, InferCtxt, SubregionOrigin}; use rustc_errors::{struct_span_err, DiagnosticBuilder}; +use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Region}; @@ -107,14 +108,37 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { infer::Subtype(box trace) => { let terr = TypeError::RegionsDoesNotOutlive(sup, sub); let mut err = self.report_and_explain_type_error(trace, &terr); - note_and_explain_region(self.tcx, &mut err, "", sup, "..."); - note_and_explain_region( - self.tcx, - &mut err, - "...does not necessarily outlive ", - sub, - "", - ); + match (sub, sup) { + (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} + (ty::RePlaceholder(_), _) => { + note_and_explain_region( + self.tcx, + &mut err, + "", + sup, + " doesn't meet the lifetime requirements", + ); + } + (_, ty::RePlaceholder(_)) => { + note_and_explain_region( + self.tcx, + &mut err, + "the required lifetime does not necessarily outlive ", + sub, + "", + ); + } + _ => { + note_and_explain_region(self.tcx, &mut err, "", sup, "..."); + note_and_explain_region( + self.tcx, + &mut err, + "...does not necessarily outlive ", + sub, + "", + ); + } + } err } infer::Reborrow(span) => { @@ -286,13 +310,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { sup: Region<'tcx>, ) -> DiagnosticBuilder<'tcx> { // I can't think how to do better than this right now. -nikomatsakis + debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); match placeholder_origin { + infer::Subtype(box ref trace) + if matches!( + &trace.cause.code.peel_derives(), + ObligationCauseCode::BindingObligation(..) + ) => + { + // Hack to get around the borrow checker because trace.cause has an `Rc`. + if let ObligationCauseCode::BindingObligation(_, span) = + &trace.cause.code.peel_derives() + { + let span = *span; + let mut err = self.report_concrete_failure(placeholder_origin, sub, sup); + err.span_note(span, "the lifetime requirement is introduced here"); + err + } else { + unreachable!() + } + } infer::Subtype(box trace) => { let terr = TypeError::RegionsPlaceholderMismatch; - self.report_and_explain_type_error(trace, &terr) + return self.report_and_explain_type_error(trace, &terr); } - - _ => self.report_concrete_failure(placeholder_origin, sub, sup), + _ => return self.report_concrete_failure(placeholder_origin, sub, sup), } } } diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index 32f73237dd..728dc2de37 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -93,10 +93,7 @@ impl<'tcx> FreeRegionMap<'tcx> { /// True for free regions other than `'static`. pub fn is_free(&self, r: Region<'_>) -> bool { - match *r { - ty::ReEarlyBound(_) | ty::ReFree(_) => true, - _ => false, - } + matches!(r, ty::ReEarlyBound(_) | ty::ReFree(_)) } /// True if `r` is a free region or static of the sort that this diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index d7b2ce7ee2..ab34cda8cc 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -393,10 +393,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { if self.expand_node(a_region, b_vid, b_data) { changes.push(b_vid); } - match *b_data { - VarValue::Value(ReStatic) | VarValue::ErrorValue => false, - _ => true, - } + !matches!(b_data, VarValue::Value(ReStatic) | VarValue::ErrorValue) }); } } @@ -972,11 +969,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } VerifyBound::IsEmpty => { - if let ty::ReEmpty(_) = min { - true - } else { - false - } + matches!(min, ty::ReEmpty(_)) } VerifyBound::AnyBound(bs) => { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 069f708856..09eecd715f 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -458,11 +458,11 @@ pub enum RegionVariableOrigin { /// This origin is used for the inference variables that we create /// during NLL region processing. - NLL(NLLRegionVariableOrigin), + Nll(NllRegionVariableOrigin), } #[derive(Copy, Clone, Debug)] -pub enum NLLRegionVariableOrigin { +pub enum NllRegionVariableOrigin { /// During NLL region processing, we create variables for free /// regions that we encounter in the function signature and /// elsewhere. This origin indices we've got one of those. @@ -1078,17 +1078,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } /// Just a convenient wrapper of `next_region_var` for using during NLL. - pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin) -> ty::Region<'tcx> { - self.next_region_var(RegionVariableOrigin::NLL(origin)) + pub fn next_nll_region_var(&self, origin: NllRegionVariableOrigin) -> ty::Region<'tcx> { + self.next_region_var(RegionVariableOrigin::Nll(origin)) } /// Just a convenient wrapper of `next_region_var` for using during NLL. pub fn next_nll_region_var_in_universe( &self, - origin: NLLRegionVariableOrigin, + origin: NllRegionVariableOrigin, universe: ty::UniverseIndex, ) -> ty::Region<'tcx> { - self.next_region_var_in_universe(RegionVariableOrigin::NLL(origin), universe) + self.next_region_var_in_universe(RegionVariableOrigin::Nll(origin), universe) } pub fn var_for_def(&self, span: Span, param: &ty::GenericParamDef) -> GenericArg<'tcx> { @@ -1317,7 +1317,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { T: TypeFoldable<'tcx>, { if !value.needs_infer() { - return value.clone(); // Avoid duplicated subst-folding. + return value; // Avoid duplicated subst-folding. } let mut r = resolve::OpportunisticVarResolver::new(self); value.fold_with(&mut r) @@ -1533,7 +1533,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // Note: if these two lines are combined into one we get // dynamic borrow errors on `self.inner`. let known = self.inner.borrow_mut().type_variables().probe(v).known(); - known.map(|t| self.shallow_resolve_ty(t)).unwrap_or(typ) + known.map_or(typ, |t| self.shallow_resolve_ty(t)) } ty::Infer(ty::IntVar(v)) => self @@ -1770,7 +1770,7 @@ impl RegionVariableOrigin { | LateBoundRegion(a, ..) | UpvarRegion(_, a) => a, BoundRegionInCoherence(_) => rustc_span::DUMMY_SP, - NLL(..) => bug!("NLL variable used with `span`"), + Nll(..) => bug!("NLL variable used with `span`"), } } } diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index de98cccf25..07c75d50d9 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -6,7 +6,6 @@ pub mod verify; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty; -use rustc_middle::ty::fold::TypeFoldable; pub fn explicit_outlives_bounds<'tcx>( param_env: ty::ParamEnv<'tcx>, @@ -15,20 +14,20 @@ pub fn explicit_outlives_bounds<'tcx>( param_env .caller_bounds() .into_iter() - .map(ty::Predicate::skip_binders) - .filter(|atom| !atom.has_escaping_bound_vars()) - .filter_map(move |atom| match atom { - ty::PredicateAtom::Projection(..) - | ty::PredicateAtom::Trait(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::TypeOutlives(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, - ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => { + .map(ty::Predicate::kind) + .filter_map(ty::Binder::no_bound_vars) + .filter_map(move |kind| match kind { + ty::PredicateKind::Projection(..) + | ty::PredicateKind::Trait(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => { Some(OutlivesBound::RegionSubRegion(r_b, r_a)) } }) diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index a676c5e65a..6687198515 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -100,7 +100,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { self.fields.obligations.push(Obligation::new( self.fields.trace.cause.clone(), self.fields.param_env, - ty::PredicateAtom::Subtype(ty::SubtypePredicate { + ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: self.a_is_expected, a, b, diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 8273c2d291..13cf1e1083 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -9,13 +9,8 @@ pub fn anonymize_predicate<'tcx>( tcx: TyCtxt<'tcx>, pred: ty::Predicate<'tcx>, ) -> ty::Predicate<'tcx> { - match *pred.kind() { - ty::PredicateKind::ForAll(binder) => { - let new = ty::PredicateKind::ForAll(tcx.anonymize_late_bound_regions(binder)); - tcx.reuse_or_mk_predicate(pred, new) - } - ty::PredicateKind::Atom(_) => pred, - } + let new = tcx.anonymize_late_bound_regions(pred.kind()); + tcx.reuse_or_mk_predicate(pred, new) } struct PredicateSet<'tcx> { @@ -126,9 +121,9 @@ impl Elaborator<'tcx> { fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) { let tcx = self.visited.tcx; - let bound_predicate = obligation.predicate.bound_atom(); + let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(data, _) => { + ty::PredicateKind::Trait(data, _) => { // Get predicates declared on the trait. let predicates = tcx.super_predicates_of(data.def_id()); @@ -150,36 +145,36 @@ impl Elaborator<'tcx> { self.stack.extend(obligations); } - ty::PredicateAtom::WellFormed(..) => { + ty::PredicateKind::WellFormed(..) => { // Currently, we do not elaborate WF predicates, // although we easily could. } - ty::PredicateAtom::ObjectSafe(..) => { + ty::PredicateKind::ObjectSafe(..) => { // Currently, we do not elaborate object-safe // predicates. } - ty::PredicateAtom::Subtype(..) => { + ty::PredicateKind::Subtype(..) => { // Currently, we do not "elaborate" predicates like `X <: Y`, // though conceivably we might. } - ty::PredicateAtom::Projection(..) => { + ty::PredicateKind::Projection(..) => { // Nothing to elaborate in a projection predicate. } - ty::PredicateAtom::ClosureKind(..) => { + ty::PredicateKind::ClosureKind(..) => { // Nothing to elaborate when waiting for a closure's kind to be inferred. } - ty::PredicateAtom::ConstEvaluatable(..) => { + ty::PredicateKind::ConstEvaluatable(..) => { // Currently, we do not elaborate const-evaluatable // predicates. } - ty::PredicateAtom::ConstEquate(..) => { + ty::PredicateKind::ConstEquate(..) => { // Currently, we do not elaborate const-equate // predicates. } - ty::PredicateAtom::RegionOutlives(..) => { + ty::PredicateKind::RegionOutlives(..) => { // Nothing to elaborate from `'a: 'b`. } - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { // We know that `T: 'a` for some type `T`. We can // often elaborate this. For example, if we know that // `[U]: 'a`, that implies that `U: 'a`. Similarly, if @@ -209,7 +204,7 @@ impl Elaborator<'tcx> { if r.is_late_bound() { None } else { - Some(ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate( + Some(ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( r, r_min, ))) } @@ -217,7 +212,7 @@ impl Elaborator<'tcx> { Component::Param(p) => { let ty = tcx.mk_ty_param(p.index, p.name); - Some(ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate( + Some(ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( ty, r_min, ))) } @@ -242,7 +237,7 @@ impl Elaborator<'tcx> { }), ); } - ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + ty::PredicateKind::TypeWellFormedFromEnv(..) => { // Nothing to elaborate } } diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 0935eb2bd7..f2b69da3f8 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,4 +1,5 @@ #![feature(bool_to_option)] +#![feature(box_patterns)] #![feature(box_syntax)] #![feature(internal_output_capture)] #![feature(nll)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 61ebd6d219..56aa3939b2 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -33,7 +33,7 @@ use rustc_session::lint; use rustc_session::output::{filename_for_input, filename_for_metadata}; use rustc_session::search_paths::PathKind; use rustc_session::Session; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{FileName, RealFileName}; use rustc_trait_selection::traits; use rustc_typeck as typeck; @@ -211,8 +211,13 @@ pub fn register_plugins<'a>( Ok((krate, lint_store)) } -fn pre_expansion_lint(sess: &Session, lint_store: &LintStore, krate: &ast::Crate) { - sess.time("pre_AST_expansion_lint_checks", || { +fn pre_expansion_lint( + sess: &Session, + lint_store: &LintStore, + krate: &ast::Crate, + crate_name: &str, +) { + sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", crate_name).run(|| { rustc_lint::check_ast_crate( sess, lint_store, @@ -233,10 +238,10 @@ fn configure_and_expand_inner<'a>( metadata_loader: &'a MetadataLoaderDyn, ) -> Result<(ast::Crate, Resolver<'a>)> { tracing::trace!("configure_and_expand_inner"); - pre_expansion_lint(sess, lint_store, &krate); + pre_expansion_lint(sess, lint_store, &krate, crate_name); let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas); - rustc_builtin_macros::register_builtin_macros(&mut resolver, sess.edition()); + rustc_builtin_macros::register_builtin_macros(&mut resolver); krate = sess.time("crate_injection", || { let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s)); @@ -295,7 +300,9 @@ fn configure_and_expand_inner<'a>( ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) }; - let extern_mod_loaded = |k: &ast::Crate| pre_expansion_lint(sess, lint_store, k); + let extern_mod_loaded = |k: &ast::Crate, ident: Ident| { + pre_expansion_lint(sess, lint_store, k, &*ident.name.as_str()) + }; let mut ecx = ExtCtxt::new(&sess, cfg, &mut resolver, Some(&extern_mod_loaded)); // Expand macros now! @@ -797,12 +804,6 @@ pub fn create_global_ctxt<'tcx>( }) }); - // Do some initialization of the DepGraph that can only be done with the tcx available. - let icx = ty::tls::ImplicitCtxt::new(&gcx); - ty::tls::enter_context(&icx, |_| { - icx.tcx.sess.time("dep_graph_tcx_init", || rustc_incremental::dep_graph_tcx_init(icx.tcx)); - }); - QueryContext(gcx) } @@ -889,7 +890,7 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { // Avoid overwhelming user with errors if borrow checking failed. // I'm not sure how helpful this is, to be honest, but it avoids a - // lot of annoying errors in the compile-fail tests (basically, + // lot of annoying errors in the ui tests (basically, // lint warnings and so on -- kindck used to do this abort, but // kindck is gone now). -nmatsakis if sess.has_errors() { @@ -1013,6 +1014,16 @@ pub fn start_codegen<'tcx>( codegen_backend.codegen_crate(tcx, metadata, need_metadata_module) }); + // Don't run these test assertions when not doing codegen. Compiletest tries to build + // build-fail tests in check mode first and expects it to not give an error in that case. + if tcx.sess.opts.output_types.should_codegen() { + rustc_incremental::assert_module_sources::assert_module_sources(tcx); + rustc_symbol_mangling::test::report_symbol_names(tcx); + } + + tcx.sess.time("assert_dep_graph", || rustc_incremental::assert_dep_graph(tcx)); + tcx.sess.time("serialize_dep_graph", || rustc_incremental::save_dep_graph(tcx)); + info!("Post-codegen\n{:?}", tcx.debug_stats()); if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 6ea0828cea..ac6b6d0311 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -280,7 +280,7 @@ impl<'tcx> Queries<'tcx> { // Don't do code generation if there were any errors self.session().compile_status()?; - // Hook for compile-fail tests. + // Hook for UI tests. Self::check_for_rustc_errors_attr(tcx); Ok(passes::start_codegen(&***self.codegen_backend(), tcx, &*outputs.peek())) @@ -289,7 +289,7 @@ impl<'tcx> Queries<'tcx> { } /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used - /// to write compile-fail tests that actually test that compilation succeeds without reporting + /// to write UI tests that actually test that compilation succeeds without reporting /// an error. fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { let def_id = match tcx.entry_fn(LOCAL_CRATE) { @@ -403,6 +403,7 @@ impl Linker { return Ok(()); } + let _timer = sess.prof.verbose_generic_activity("link_crate"); self.codegen_backend.link(&self.sess, codegen_results, &self.prepare_outputs) } } @@ -416,9 +417,19 @@ impl Compiler { let queries = Queries::new(&self); let ret = f(&queries); - if self.session().opts.debugging_opts.query_stats { - if let Ok(gcx) = queries.global_ctxt() { - gcx.peek_mut().print_stats(); + // NOTE: intentionally does not compute the global context if it hasn't been built yet, + // since that likely means there was a parse error. + if let Some(Ok(gcx)) = &mut *queries.global_ctxt.result.borrow_mut() { + // We assume that no queries are run past here. If there are new queries + // after this point, they'll show up as "" in self-profiling data. + { + let _prof_timer = + queries.session().prof.generic_activity("self_profile_alloc_query_strings"); + gcx.enter(|tcx| tcx.alloc_self_profile_query_strings()); + } + + if self.session().opts.debugging_opts.query_stats { + gcx.print_stats(); } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 3e94f16377..f9c3406d3b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -7,20 +7,20 @@ use rustc_session::config::{build_configuration, build_session_options, to_crate use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; use rustc_session::config::{ - Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion, + Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion, WasiExecModel, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; -use rustc_session::utils::NativeLibKind; +use rustc_session::utils::{CanonicalizedPath, NativeLibKind}; use rustc_session::{build_session, getopts, DiagnosticOutput, Session}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_span::symbol::sym; use rustc_span::SourceFileHashAlgorithm; use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy}; -use rustc_target::spec::{RelocModel, RelroLevel, TlsModel}; +use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TlsModel}; use std::collections::{BTreeMap, BTreeSet}; use std::iter::FromIterator; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; type CfgSpecs = FxHashSet<(String, Option)>; @@ -50,7 +50,8 @@ where S: Into, I: IntoIterator, { - let locations: BTreeSet<_> = locations.into_iter().map(|s| s.into()).collect(); + let locations: BTreeSet = + locations.into_iter().map(|s| CanonicalizedPath::new(Path::new(&s.into()))).collect(); ExternEntry { location: ExternLocation::ExactPaths(locations), @@ -446,6 +447,7 @@ fn test_codegen_options_tracking_hash() { tracked!(profile_use, Some(PathBuf::from("abc"))); tracked!(relocation_model, Some(RelocModel::Pic)); tracked!(soft_float, true); + tracked!(split_debuginfo, Some(SplitDebuginfo::Packed)); tracked!(target_cpu, Some(String::from("abc"))); tracked!(target_feature, String::from("all the features, all of them")); } @@ -538,6 +540,7 @@ fn test_debugging_options_tracking_hash() { // This list is in alphabetical order. tracked!(allow_features, Some(vec![String::from("lang_items")])); tracked!(always_encode_mir, true); + tracked!(assume_incomplete_release, true); tracked!(asm_comments, true); tracked!(binary_dep_depinfo, true); tracked!(chalk, true); @@ -579,7 +582,6 @@ fn test_debugging_options_tracking_hash() { tracked!(relax_elf_relocations, Some(true)); tracked!(relro_level, Some(RelroLevel::Full)); tracked!(report_delayed_bugs, true); - tracked!(run_dsymutil, false); tracked!(sanitizer, SanitizerSet::ADDRESS); tracked!(sanitizer_memory_track_origins, 2); tracked!(sanitizer_recover, SanitizerSet::ADDRESS); @@ -597,6 +599,7 @@ fn test_debugging_options_tracking_hash() { tracked!(unleash_the_miri_inside_of_you, true); tracked!(use_ctors_section, Some(true)); tracked!(verify_llvm_ir, true); + tracked!(wasi_exec_model, Some(WasiExecModel::Reactor)); } #[test] diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index f34990a1a1..b7dc539c6d 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -759,7 +759,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { fn visit_item_kind(&mut self, i: &mut ast::ItemKind) { let is_const = match i { ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true, - ast::ItemKind::Fn(_, ref sig, _, _) => Self::is_sig_const(sig), + ast::ItemKind::Fn(box ast::FnKind(_, ref sig, _, _)) => Self::is_sig_const(sig), _ => false, }; self.run(is_const, |s| noop_visit_item_kind(i, s)) @@ -768,7 +768,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { fn flat_map_trait_item(&mut self, i: P) -> SmallVec<[P; 1]> { let is_const = match i.kind { ast::AssocItemKind::Const(..) => true, - ast::AssocItemKind::Fn(_, ref sig, _, _) => Self::is_sig_const(sig), + ast::AssocItemKind::Fn(box ast::FnKind(_, ref sig, _, _)) => Self::is_sig_const(sig), _ => false, }; self.run(is_const, |s| noop_flat_map_assoc_item(i, s)) diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index c0045d3f79..297f3d19ca 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -33,7 +33,7 @@ impl<'a> Cursor<'a> { #[cfg(not(debug_assertions))] { - '\0' + EOF_CHAR } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 676c85e4af..2cedef6251 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -43,6 +43,7 @@ use rustc_index::vec::Idx; use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::{GenericArgKind, Subst}; +use rustc_middle::ty::Instance; use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::edition::Edition; @@ -95,18 +96,24 @@ fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr { impl EarlyLintPass for WhileTrue { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if let ast::ExprKind::While(cond, ..) = &e.kind { + if let ast::ExprKind::While(cond, _, label) = &e.kind { if let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind { if let ast::LitKind::Bool(true) = lit.kind { if !lit.span.from_expansion() { let msg = "denote infinite loops with `loop { ... }`"; - let condition_span = cx.sess.source_map().guess_head_span(e.span); + let condition_span = e.span.with_hi(cond.span.hi()); cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| { lint.build(msg) .span_suggestion_short( condition_span, "use `loop`", - "loop".to_owned(), + format!( + "{}loop", + label.map_or_else(String::new, |label| format!( + "{}: ", + label.ident, + )) + ), Applicability::MachineApplicable, ) .emit(); @@ -350,17 +357,15 @@ impl EarlyLintPass for UnsafeCode { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { match it.kind { - ast::ItemKind::Trait(_, ast::Unsafe::Yes(_), ..) => { - self.report_unsafe(cx, it.span, |lint| { + ast::ItemKind::Trait(box ast::TraitKind(_, ast::Unsafe::Yes(_), ..)) => self + .report_unsafe(cx, it.span, |lint| { lint.build("declaration of an `unsafe` trait").emit() - }) - } + }), - ast::ItemKind::Impl { unsafety: ast::Unsafe::Yes(_), .. } => { - self.report_unsafe(cx, it.span, |lint| { + ast::ItemKind::Impl(box ast::ImplKind { unsafety: ast::Unsafe::Yes(_), .. }) => self + .report_unsafe(cx, it.span, |lint| { lint.build("implementation of an `unsafe` trait").emit() - }) - } + }), _ => {} } @@ -541,7 +546,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { return; } } - hir::ItemKind::Impl { of_trait: Some(ref trait_ref), items, .. } => { + hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), items, .. }) => { // If the trait is private, add the impl items to `private_traits` so they don't get // reported for missing docs. let real_trait = trait_ref.path.res.def_id(); @@ -865,10 +870,10 @@ declare_lint_pass!( impl EarlyLintPass for AnonymousParameters { fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { - if let ast::AssocItemKind::Fn(_, ref sig, _, _) = it.kind { + if let ast::AssocItemKind::Fn(box FnKind(_, ref sig, _, _)) = it.kind { for arg in sig.decl.inputs.iter() { if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { - if ident.name == kw::Invalid { + if ident.name == kw::Empty { cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| { let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span); @@ -938,8 +943,8 @@ impl EarlyLintPass for DeprecatedAttr { if attr.ident().map(|ident| ident.name) == Some(n) { if let &AttributeGate::Gated( Stability::Deprecated(link, suggestion), - ref name, - ref reason, + name, + reason, _, ) = g { @@ -1549,13 +1554,13 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { use rustc_middle::ty::fold::TypeFoldable; - use rustc_middle::ty::PredicateAtom::*; + use rustc_middle::ty::PredicateKind::*; if cx.tcx.features().trivial_bounds { let def_id = cx.tcx.hir().local_def_id(item.hir_id); let predicates = cx.tcx.predicates_of(def_id); for &(predicate, span) in predicates.predicates { - let predicate_kind_name = match predicate.skip_binders() { + let predicate_kind_name = match predicate.kind().skip_binder() { Trait(..) => "Trait", TypeOutlives(..) | RegionOutlives(..) => "Lifetime", @@ -1935,8 +1940,8 @@ impl ExplicitOutlivesRequirements { ) -> Vec> { inferred_outlives .iter() - .filter_map(|(pred, _)| match pred.skip_binders() { - ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(a, b)) => match a { + .filter_map(|(pred, _)| match pred.kind().skip_binder() { + ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match a { ty::ReEarlyBound(ebr) if ebr.index == index => Some(b), _ => None, }, @@ -1951,8 +1956,8 @@ impl ExplicitOutlivesRequirements { ) -> Vec> { inferred_outlives .iter() - .filter_map(|(pred, _)| match pred.skip_binders() { - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(a, b)) => { + .filter_map(|(pred, _)| match pred.kind().skip_binder() { + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => { a.is_param(index).then_some(b) } _ => None, @@ -2299,7 +2304,7 @@ impl EarlyLintPass for IncompleteFeatures { } } -const HAS_MIN_FEATURES: &[Symbol] = &[sym::const_generics, sym::specialization]; +const HAS_MIN_FEATURES: &[Symbol] = &[sym::specialization]; declare_lint! { /// The `invalid_value` lint detects creating a value that is not valid, @@ -2595,6 +2600,11 @@ declare_lint! { } pub struct ClashingExternDeclarations { + /// Map of function symbol name to the first-seen hir id for that symbol name.. If seen_decls + /// contains an entry for key K, it means a symbol with name K has been seen by this lint and + /// the symbol should be reported as a clashing declaration. + // FIXME: Technically, we could just store a &'tcx str here without issue; however, the + // `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime. seen_decls: FxHashMap, } @@ -2626,16 +2636,17 @@ impl ClashingExternDeclarations { fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option { let hid = fi.hir_id; - let name = - &tcx.codegen_fn_attrs(tcx.hir().local_def_id(hid)).link_name.unwrap_or(fi.ident.name); - - if self.seen_decls.contains_key(name) { + let local_did = tcx.hir().local_def_id(fi.hir_id); + let did = local_did.to_def_id(); + let instance = Instance::new(did, ty::List::identity_for_item(tcx, did)); + let name = Symbol::intern(tcx.symbol_name(instance).name); + if let Some(&hir_id) = self.seen_decls.get(&name) { // Avoid updating the map with the new entry when we do find a collision. We want to // make sure we're always pointing to the first definition as the previous declaration. // This lets us avoid emitting "knock-on" diagnostics. - Some(*self.seen_decls.get(name).unwrap()) + Some(hir_id) } else { - self.seen_decls.insert(*name, hid) + self.seen_decls.insert(name, hid) } } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 780bf5fecf..58a9064b91 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -262,6 +262,7 @@ impl LintStore { } } + #[track_caller] pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { let target = match self.by_name.get(new_name) { Some(&Id(lint_id)) => lint_id, @@ -368,10 +369,23 @@ impl LintStore { lint_name.to_string() }; // If the lint was scoped with `tool::` check if the tool lint exists - if tool_name.is_some() { + if let Some(tool_name) = tool_name { match self.by_name.get(&complete_name) { None => match self.lint_groups.get(&*complete_name) { - None => return CheckLintNameResult::Tool(Err((None, String::new()))), + // If the lint isn't registered, there are two possibilities: + None => { + // 1. The tool is currently running, so this lint really doesn't exist. + // FIXME: should this handle tools that never register a lint, like rustfmt? + tracing::debug!("lints={:?}", self.by_name.keys().collect::>()); + let tool_prefix = format!("{}::", tool_name); + return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) { + self.no_lint_suggestion(&complete_name) + } else { + // 2. The tool isn't currently running, so no lints will be registered. + // To avoid giving a false positive, ignore all unknown lints. + CheckLintNameResult::Tool(Err((None, String::new()))) + }; + } Some(LintGroup { lint_ids, .. }) => { return CheckLintNameResult::Tool(Ok(&lint_ids)); } @@ -388,7 +402,7 @@ impl LintStore { Some(new_name.to_owned()), ), Some(&Removed(ref reason)) => CheckLintNameResult::Warning( - format!("lint `{}` has been removed: `{}`", complete_name, reason), + format!("lint `{}` has been removed: {}", complete_name, reason), None, ), None => match self.lint_groups.get(&*complete_name) { @@ -412,6 +426,21 @@ impl LintStore { } } + fn no_lint_suggestion(&self, lint_name: &str) -> CheckLintNameResult<'_> { + let name_lower = lint_name.to_lowercase(); + let symbols = + self.get_lints().iter().map(|l| Symbol::intern(&l.name_lower())).collect::>(); + + if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_ok() { + // First check if the lint name is (partly) in upper case instead of lower case... + CheckLintNameResult::NoLint(Some(Symbol::intern(&name_lower))) + } else { + // ...if not, search for lints with a similar name + let suggestion = find_best_match_for_name(&symbols, Symbol::intern(&name_lower), None); + CheckLintNameResult::NoLint(suggestion) + } + } + fn check_tool_name_for_backwards_compat( &self, lint_name: &str, @@ -421,18 +450,7 @@ impl LintStore { match self.by_name.get(&complete_name) { None => match self.lint_groups.get(&*complete_name) { // Now we are sure, that this lint exists nowhere - None => { - let symbols = - self.by_name.keys().map(|name| Symbol::intern(&name)).collect::>(); - - let suggestion = find_best_match_for_name( - &symbols, - Symbol::intern(&lint_name.to_lowercase()), - None, - ); - - CheckLintNameResult::NoLint(suggestion) - } + None => self.no_lint_suggestion(lint_name), Some(LintGroup { lint_ids, depr, .. }) => { // Reaching this would be weird, but let's cover this case anyway if let Some(LintAlias { name, silent }) = depr { @@ -611,6 +629,13 @@ pub trait LintContext: Sized { db.help("to document an item produced by a macro, \ the macro must produce the documentation as part of its expansion"); } + BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident) => { + db.span_suggestion(span, "remove `mut` from the parameter", ident.to_string(), Applicability::MachineApplicable); + } + BuiltinLintDiagnostics::MissingAbi(span, default_abi) => { + db.span_label(span, "ABI should be specified here"); + db.help(&format!("the default ABI is {}", default_abi.name())); + } } // Rewrap `db`, and pass control to the user. decorate(LintDiagnosticBuilder::new(db)); @@ -736,6 +761,14 @@ impl<'tcx> LateContext<'tcx> { hir::QPath::Resolved(_, ref path) => path.res, hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self .maybe_typeck_results() + .filter(|typeck_results| typeck_results.hir_owner == id.owner) + .or_else(|| { + if self.tcx.has_typeck_results(id.owner.to_def_id()) { + Some(self.tcx.typeck(id.owner)) + } else { + None + } + }) .and_then(|typeck_results| typeck_results.type_dependent_def(id)) .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), } @@ -743,7 +776,7 @@ impl<'tcx> LateContext<'tcx> { /// Check if a `DefId`'s path matches the given absolute type path usage. /// - /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`; + /// Anonymous scopes such as `extern` imports are matched with `kw::Empty`; /// inherent `impl` blocks are matched with the name of the type. /// /// Instead of using this method, it is often preferable to instead use diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 08c147ec3a..231edf442e 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -143,6 +143,14 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> run_early_pass!(self, check_fn, fk, span, id); self.check_id(id); ast_visit::walk_fn(self, fk, span); + + // Explicitly check for lints associated with 'closure_id', since + // it does not have a corresponding AST node + if let ast_visit::FnKind::Fn(_, _, sig, _, _) = fk { + if let ast::Async::Yes { closure_id, .. } = sig.header.asyncness { + self.check_id(closure_id); + } + } run_early_pass!(self, check_fn_post, fk, span, id); } @@ -208,6 +216,14 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_expr_post(&mut self, e: &'a ast::Expr) { run_early_pass!(self, check_expr_post, e); + + // Explicitly check for lints associated with 'closure_id', since + // it does not have a corresponding AST node + match e.kind { + ast::ExprKind::Closure(_, ast::Async::Yes { closure_id, .. }, ..) + | ast::ExprKind::Async(_, closure_id, ..) => self.check_id(closure_id), + _ => {} + } } fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) { @@ -379,17 +395,9 @@ pub fn check_ast_crate( // All of the buffered lints should have been emitted at this point. // If not, that means that we somehow buffered a lint for a node id // that was not lint-checked (perhaps it doesn't exist?). This is a bug. - // - // Rustdoc runs everybody-loops before the early lints and removes - // function bodies, so it's totally possible for linted - // node ids to not exist (e.g., macros defined within functions for the - // unused_macro lint) anymore. So we only run this check - // when we're not in rustdoc mode. (see issue #47639) - if !sess.opts.actually_rustdoc { - for (_id, lints) in buffered.map { - for early_lint in lints { - sess.delay_span_bug(early_lint.span, "failed to process buffered lint here"); - } + for (_id, lints) in buffered.map { + for early_lint in lints { + sess.delay_span_bug(early_lint.span, "failed to process buffered lint here"); } } } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index af5972c6c8..26e536e8f1 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -2,7 +2,7 @@ //! Clippy. use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_ast::{Item, ItemKind}; +use rustc_ast::{ImplKind, Item, ItemKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -243,7 +243,7 @@ declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]); impl EarlyLintPass for LintPassImpl { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::Impl { of_trait: Some(lint_pass), .. } = &item.kind { + if let ItemKind::Impl(box ImplKind { of_trait: Some(lint_pass), .. }) = &item.kind { if let Some(last) = lint_pass.path.segments.last() { if last.ident.name == sym::LintPass { let expn_data = lint_pass.path.span.ctxt().outer_expn_data(); diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 015e109871..3821a393ef 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -140,6 +140,8 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { let generics = self.context.generics.take(); self.context.generics = it.kind.generics(); + let old_cached_typeck_results = self.context.cached_typeck_results.take(); + let old_enclosing_body = self.context.enclosing_body.take(); self.with_lint_attrs(it.hir_id, &it.attrs, |cx| { cx.with_param_env(it.hir_id, |cx| { lint_callback!(cx, check_item, it); @@ -147,6 +149,8 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas lint_callback!(cx, check_item_post, it); }); }); + self.context.enclosing_body = old_enclosing_body; + self.context.cached_typeck_results.set(old_cached_typeck_results); self.context.generics = generics; } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 570578ff08..1fc2bd0916 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -10,7 +10,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::{intravisit, HirId}; use rustc_middle::hir::map::Map; -use rustc_middle::lint::LevelSource; +use rustc_middle::lint::LevelAndSource; use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::lint::{ struct_lint_level, LintLevelMap, LintLevelSets, LintLevelSource, LintSet, @@ -112,9 +112,9 @@ impl<'s> LintLevelsBuilder<'s> { /// diagnostic with no change to `specs`. fn insert_spec( &mut self, - specs: &mut FxHashMap, + specs: &mut FxHashMap, id: LintId, - (level, src): LevelSource, + (level, src): LevelAndSource, ) { // Setting to a non-forbid level is an error if the lint previously had // a forbid level. Note that this is not necessarily true even with a @@ -356,8 +356,7 @@ impl<'s> LintLevelsBuilder<'s> { |lint| { let msg = format!( "lint name `{}` is deprecated \ - and may not have an effect in the future. \ - Also `cfg_attr(cargo-clippy)` won't be necessary anymore", + and may not have an effect in the future.", name ); lint.build(&msg) @@ -426,6 +425,11 @@ impl<'s> LintLevelsBuilder<'s> { src, Some(li.span().into()), |lint| { + let name = if let Some(tool_name) = tool_name { + format!("{}::{}", tool_name, name) + } else { + name.to_string() + }; let mut db = lint.build(&format!("unknown lint: `{}`", name)); if let Some(suggestion) = suggestion { db.span_suggestion( diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 2336b52619..638b73c27a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -30,6 +30,7 @@ #![feature(array_windows)] #![feature(bool_to_option)] #![feature(box_syntax)] +#![feature(box_patterns)] #![feature(crate_visibility_modifier)] #![feature(iter_order_by)] #![feature(never_type)] @@ -54,8 +55,8 @@ mod late; mod levels; mod methods; mod non_ascii_idents; +mod non_fmt_panic; mod nonstandard_style; -mod panic_fmt; mod passes; mod redundant_semicolon; mod traits; @@ -80,8 +81,8 @@ use builtin::*; use internal::*; use methods::*; use non_ascii_idents::*; +use non_fmt_panic::NonPanicFmt; use nonstandard_style::*; -use panic_fmt::PanicFmt; use redundant_semicolon::*; use traits::*; use types::*; @@ -168,7 +169,7 @@ macro_rules! late_lint_passes { ClashingExternDeclarations: ClashingExternDeclarations::new(), DropTraitConstraints: DropTraitConstraints, TemporaryCStringAsPtr: TemporaryCStringAsPtr, - PanicFmt: PanicFmt, + NonPanicFmt: NonPanicFmt, ] ); }; diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs new file mode 100644 index 0000000000..e98297b692 --- /dev/null +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -0,0 +1,197 @@ +use crate::{LateContext, LateLintPass, LintContext}; +use rustc_ast as ast; +use rustc_errors::{pluralize, Applicability}; +use rustc_hir as hir; +use rustc_middle::ty; +use rustc_parse_format::{ParseMode, Parser, Piece}; +use rustc_span::{sym, symbol::kw, InnerSpan, Span, Symbol}; + +declare_lint! { + /// The `non_fmt_panic` lint detects `panic!(..)` invocations where the first + /// argument is not a formatting string. + /// + /// ### Example + /// + /// ```rust,no_run + /// panic!("{}"); + /// panic!(123); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In Rust 2018 and earlier, `panic!(x)` directly uses `x` as the message. + /// That means that `panic!("{}")` panics with the message `"{}"` instead + /// of using it as a formatting string, and `panic!(123)` will panic with + /// an `i32` as message. + /// + /// Rust 2021 always interprets the first argument as format string. + NON_FMT_PANIC, + Warn, + "detect single-argument panic!() invocations in which the argument is not a format string", + report_in_external_macro +} + +declare_lint_pass!(NonPanicFmt => [NON_FMT_PANIC]); + +impl<'tcx> LateLintPass<'tcx> for NonPanicFmt { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::Call(f, [arg]) = &expr.kind { + if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() { + if Some(def_id) == cx.tcx.lang_items().begin_panic_fn() + || Some(def_id) == cx.tcx.lang_items().panic_fn() + || Some(def_id) == cx.tcx.lang_items().panic_str() + { + if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id { + if cx.tcx.is_diagnostic_item(sym::std_panic_2015_macro, id) + || cx.tcx.is_diagnostic_item(sym::core_panic_2015_macro, id) + { + check_panic(cx, f, arg); + } + } + } + } + } + } +} + +fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::Lit(lit) = &arg.kind { + if let ast::LitKind::Str(sym, _) = lit.node { + // The argument is a string literal. + check_panic_str(cx, f, arg, &sym.as_str()); + return; + } + } + + // The argument is *not* a string literal. + + let (span, panic) = panic_call(cx, f); + + cx.struct_span_lint(NON_FMT_PANIC, arg.span, |lint| { + let mut l = lint.build("panic message is not a string literal"); + l.note("this is no longer accepted in Rust 2021"); + if span.contains(arg.span) { + l.span_suggestion_verbose( + arg.span.shrink_to_lo(), + "add a \"{}\" format string to Display the message", + "\"{}\", ".into(), + Applicability::MaybeIncorrect, + ); + if panic == sym::std_panic_macro { + l.span_suggestion_verbose( + span.until(arg.span), + "or use std::panic::panic_any instead", + "std::panic::panic_any(".into(), + Applicability::MachineApplicable, + ); + } + } + l.emit(); + }); +} + +fn check_panic_str<'tcx>( + cx: &LateContext<'tcx>, + f: &'tcx hir::Expr<'tcx>, + arg: &'tcx hir::Expr<'tcx>, + fmt: &str, +) { + if !fmt.contains(&['{', '}'][..]) { + // No brace, no problem. + return; + } + + let fmt_span = arg.span.source_callsite(); + + let (snippet, style) = match cx.sess().parse_sess.source_map().span_to_snippet(fmt_span) { + Ok(snippet) => { + // Count the number of `#`s between the `r` and `"`. + let style = snippet.strip_prefix('r').and_then(|s| s.find('"')); + (Some(snippet), style) + } + Err(_) => (None, None), + }; + + let mut fmt_parser = + Parser::new(fmt.as_ref(), style, snippet.clone(), false, ParseMode::Format); + let n_arguments = (&mut fmt_parser).filter(|a| matches!(a, Piece::NextArgument(_))).count(); + + let (span, _) = panic_call(cx, f); + + if n_arguments > 0 && fmt_parser.errors.is_empty() { + let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] { + [] => vec![fmt_span], + v => v.iter().map(|span| fmt_span.from_inner(*span)).collect(), + }; + cx.struct_span_lint(NON_FMT_PANIC, arg_spans, |lint| { + let mut l = lint.build(match n_arguments { + 1 => "panic message contains an unused formatting placeholder", + _ => "panic message contains unused formatting placeholders", + }); + l.note("this message is not used as a format string when given without arguments, but will be in Rust 2021"); + if span.contains(arg.span) { + l.span_suggestion( + arg.span.shrink_to_hi(), + &format!("add the missing argument{}", pluralize!(n_arguments)), + ", ...".into(), + Applicability::HasPlaceholders, + ); + l.span_suggestion( + arg.span.shrink_to_lo(), + "or add a \"{}\" format string to use the message literally", + "\"{}\", ".into(), + Applicability::MachineApplicable, + ); + } + l.emit(); + }); + } else { + let brace_spans: Option> = + snippet.filter(|s| s.starts_with('"') || s.starts_with("r#")).map(|s| { + s.char_indices() + .filter(|&(_, c)| c == '{' || c == '}') + .map(|(i, _)| fmt_span.from_inner(InnerSpan { start: i, end: i + 1 })) + .collect() + }); + let msg = match &brace_spans { + Some(v) if v.len() == 1 => "panic message contains a brace", + _ => "panic message contains braces", + }; + cx.struct_span_lint(NON_FMT_PANIC, brace_spans.unwrap_or(vec![span]), |lint| { + let mut l = lint.build(msg); + l.note("this message is not used as a format string, but will be in Rust 2021"); + if span.contains(arg.span) { + l.span_suggestion( + arg.span.shrink_to_lo(), + "add a \"{}\" format string to use the message literally", + "\"{}\", ".into(), + Applicability::MachineApplicable, + ); + } + l.emit(); + }); + } +} + +fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol) { + let mut expn = f.span.ctxt().outer_expn_data(); + + let mut panic_macro = kw::Empty; + + // Unwrap more levels of macro expansion, as panic_2015!() + // was likely expanded from panic!() and possibly from + // [debug_]assert!(). + for &i in + &[sym::std_panic_macro, sym::core_panic_macro, sym::assert_macro, sym::debug_assert_macro] + { + let parent = expn.call_site.ctxt().outer_expn_data(); + if parent.macro_def_id.map_or(false, |id| cx.tcx.is_diagnostic_item(i, id)) { + expn = parent; + panic_macro = i; + } + } + + (expn.call_site, panic_macro) +} diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 6d61b86f32..121dde325f 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -56,8 +56,19 @@ declare_lint! { declare_lint_pass!(NonCamelCaseTypes => [NON_CAMEL_CASE_TYPES]); +/// Some unicode characters *have* case, are considered upper case or lower case, but they *can't* +/// be upper cased or lower cased. For the purposes of the lint suggestion, we care about being able +/// to change the char's case. fn char_has_case(c: char) -> bool { - c.is_lowercase() || c.is_uppercase() + let mut l = c.to_lowercase(); + let mut u = c.to_uppercase(); + while let Some(l) = l.next() { + match u.next() { + Some(u) if l != u => return true, + _ => {} + } + } + u.next().is_some() } fn is_camel_case(name: &str) -> bool { @@ -138,6 +149,8 @@ impl NonCamelCaseTypes { to_camel_case(name), Applicability::MaybeIncorrect, ); + } else { + err.span_label(ident.span, "should have an UpperCamelCase name"); } err.emit(); @@ -275,15 +288,32 @@ impl NonSnakeCase { // We have a valid span in almost all cases, but we don't have one when linting a crate // name provided via the command line. if !ident.span.is_dummy() { + let sc_ident = Ident::from_str_and_span(&sc, ident.span); + let (message, suggestion) = if sc_ident.is_reserved() { + // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers. + // Instead, recommend renaming the identifier entirely or, if permitted, + // escaping it to create a raw identifier. + if sc_ident.name.can_be_raw() { + ("rename the identifier or convert it to a snake case raw identifier", sc_ident.to_string()) + } else { + err.note(&format!("`{}` cannot be used as a raw identifier", sc)); + ("rename the identifier", String::new()) + } + } else { + ("convert the identifier to snake case", sc) + }; + err.span_suggestion( ident.span, - "convert the identifier to snake case", - sc, + message, + suggestion, Applicability::MaybeIncorrect, ); } else { err.help(&format!("convert the identifier to snake case: `{}`", sc)); } + } else { + err.span_label(ident.span, "should have a snake_case name"); } err.emit(); @@ -397,7 +427,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { - if let &PatKind::Binding(_, hid, ident, _) = &p.kind { + if let PatKind::Binding(_, hid, ident, _) = p.kind { if let hir::Node::Pat(parent_pat) = cx.tcx.hir().get(cx.tcx.hir().get_parent_node(hid)) { if let PatKind::Struct(_, field_pats, _) = &parent_pat.kind { @@ -462,6 +492,8 @@ impl NonUpperCaseGlobals { uc, Applicability::MaybeIncorrect, ); + } else { + err.span_label(ident.span, "should have an UPPER_CASE name"); } err.emit(); diff --git a/compiler/rustc_lint/src/panic_fmt.rs b/compiler/rustc_lint/src/panic_fmt.rs deleted file mode 100644 index e01ff1641b..0000000000 --- a/compiler/rustc_lint/src/panic_fmt.rs +++ /dev/null @@ -1,151 +0,0 @@ -use crate::{LateContext, LateLintPass, LintContext}; -use rustc_ast as ast; -use rustc_errors::{pluralize, Applicability}; -use rustc_hir as hir; -use rustc_middle::ty; -use rustc_parse_format::{ParseMode, Parser, Piece}; -use rustc_span::{sym, InnerSpan}; - -declare_lint! { - /// The `non_fmt_panic` lint detects `panic!("..")` with `{` or `}` in the string literal - /// when it is not used as a format string. - /// - /// ### Example - /// - /// ```rust,no_run - /// panic!("{}"); - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// `panic!("{}")` panics with the message `"{}"`, as a `panic!()` invocation - /// with a single argument does not use `format_args!()`. - /// A future edition of Rust will interpret this string as format string, - /// which would break this. - NON_FMT_PANIC, - Warn, - "detect braces in single-argument panic!() invocations", - report_in_external_macro -} - -declare_lint_pass!(PanicFmt => [NON_FMT_PANIC]); - -impl<'tcx> LateLintPass<'tcx> for PanicFmt { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Call(f, [arg]) = &expr.kind { - if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() { - if Some(def_id) == cx.tcx.lang_items().begin_panic_fn() - || Some(def_id) == cx.tcx.lang_items().panic_fn() - { - check_panic(cx, f, arg); - } - } - } - } -} - -fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Lit(lit) = &arg.kind { - if let ast::LitKind::Str(sym, _) = lit.node { - let mut expn = f.span.ctxt().outer_expn_data(); - if let Some(id) = expn.macro_def_id { - if cx.tcx.is_diagnostic_item(sym::std_panic_macro, id) - || cx.tcx.is_diagnostic_item(sym::core_panic_macro, id) - { - let fmt = sym.as_str(); - if !fmt.contains(&['{', '}'][..]) { - return; - } - - let fmt_span = arg.span.source_callsite(); - - let (snippet, style) = - match cx.sess().parse_sess.source_map().span_to_snippet(fmt_span) { - Ok(snippet) => { - // Count the number of `#`s between the `r` and `"`. - let style = snippet.strip_prefix('r').and_then(|s| s.find('"')); - (Some(snippet), style) - } - Err(_) => (None, None), - }; - - let mut fmt_parser = - Parser::new(fmt.as_ref(), style, snippet.clone(), false, ParseMode::Format); - let n_arguments = - (&mut fmt_parser).filter(|a| matches!(a, Piece::NextArgument(_))).count(); - - // Unwrap another level of macro expansion if this panic!() - // was expanded from assert!() or debug_assert!(). - for &assert in &[sym::assert_macro, sym::debug_assert_macro] { - let parent = expn.call_site.ctxt().outer_expn_data(); - if parent - .macro_def_id - .map_or(false, |id| cx.tcx.is_diagnostic_item(assert, id)) - { - expn = parent; - } - } - - if n_arguments > 0 && fmt_parser.errors.is_empty() { - let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] { - [] => vec![fmt_span], - v => v.iter().map(|span| fmt_span.from_inner(*span)).collect(), - }; - cx.struct_span_lint(NON_FMT_PANIC, arg_spans, |lint| { - let mut l = lint.build(match n_arguments { - 1 => "panic message contains an unused formatting placeholder", - _ => "panic message contains unused formatting placeholders", - }); - l.note("this message is not used as a format string when given without arguments, but will be in a future Rust edition"); - if expn.call_site.contains(arg.span) { - l.span_suggestion( - arg.span.shrink_to_hi(), - &format!("add the missing argument{}", pluralize!(n_arguments)), - ", ...".into(), - Applicability::HasPlaceholders, - ); - l.span_suggestion( - arg.span.shrink_to_lo(), - "or add a \"{}\" format string to use the message literally", - "\"{}\", ".into(), - Applicability::MachineApplicable, - ); - } - l.emit(); - }); - } else { - let brace_spans: Option> = snippet - .filter(|s| s.starts_with('"') || s.starts_with("r#")) - .map(|s| { - s.char_indices() - .filter(|&(_, c)| c == '{' || c == '}') - .map(|(i, _)| { - fmt_span.from_inner(InnerSpan { start: i, end: i + 1 }) - }) - .collect() - }); - let msg = match &brace_spans { - Some(v) if v.len() == 1 => "panic message contains a brace", - _ => "panic message contains braces", - }; - cx.struct_span_lint(NON_FMT_PANIC, brace_spans.unwrap_or(vec![expn.call_site]), |lint| { - let mut l = lint.build(msg); - l.note("this message is not used as a format string, but will be in a future Rust edition"); - if expn.call_site.contains(arg.span) { - l.span_suggestion( - arg.span.shrink_to_lo(), - "add a \"{}\" format string to use the message literally", - "\"{}\", ".into(), - Applicability::MachineApplicable, - ); - } - l.emit(); - }); - } - } - } - } - } -} diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index 428198cae8..0fe6564880 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -28,27 +28,19 @@ declare_lint_pass!(RedundantSemicolons => [REDUNDANT_SEMICOLONS]); impl EarlyLintPass for RedundantSemicolons { fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { - let mut after_item_stmt = false; let mut seq = None; for stmt in block.stmts.iter() { match (&stmt.kind, &mut seq) { (StmtKind::Empty, None) => seq = Some((stmt.span, false)), (StmtKind::Empty, Some(seq)) => *seq = (seq.0.to(stmt.span), true), - (_, seq) => { - maybe_lint_redundant_semis(cx, seq, after_item_stmt); - after_item_stmt = matches!(stmt.kind, StmtKind::Item(_)); - } + (_, seq) => maybe_lint_redundant_semis(cx, seq), } } - maybe_lint_redundant_semis(cx, &mut seq, after_item_stmt); + maybe_lint_redundant_semis(cx, &mut seq); } } -fn maybe_lint_redundant_semis( - cx: &EarlyContext<'_>, - seq: &mut Option<(Span, bool)>, - after_item_stmt: bool, -) { +fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) { if let Some((span, multiple)) = seq.take() { // FIXME: Find a better way of ignoring the trailing // semicolon from macro expansion @@ -56,12 +48,6 @@ fn maybe_lint_redundant_semis( return; } - // FIXME: Lint on semicolons after item statements - // once doing so doesn't break bootstrapping - if after_item_stmt { - return; - } - cx.struct_span_lint(REDUNDANT_SEMICOLONS, span, |lint| { let (msg, rem) = if multiple { ("unnecessary trailing semicolons", "remove these semicolons") diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index d4f79036e5..b031c1108c 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -45,12 +45,12 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - use rustc_middle::ty::PredicateAtom::*; + use rustc_middle::ty::PredicateKind::*; let def_id = cx.tcx.hir().local_def_id(item.hir_id); let predicates = cx.tcx.explicit_predicates_of(def_id); for &(predicate, span) in predicates.predicates { - let trait_predicate = match predicate.skip_binders() { + let trait_predicate = match predicate.kind().skip_binder() { Trait(trait_predicate, _constness) => trait_predicate, _ => continue, }; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 9ad9d53cd0..1e879d2937 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -168,25 +168,25 @@ fn lint_overflowing_range_endpoint<'tcx>( // For `isize` & `usize`, be conservative with the warnings, so that the // warnings are consistent between 32- and 64-bit platforms. -fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) { +fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) { match int_ty { - ast::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()), - ast::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()), - ast::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()), - ast::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()), - ast::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()), - ast::IntTy::I128 => (i128::MIN, i128::MAX), + ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()), + ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()), + ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()), + ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()), + ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()), + ty::IntTy::I128 => (i128::MIN, i128::MAX), } } -fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) { +fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) { let max = match uint_ty { - ast::UintTy::Usize => u64::MAX.into(), - ast::UintTy::U8 => u8::MAX.into(), - ast::UintTy::U16 => u16::MAX.into(), - ast::UintTy::U32 => u32::MAX.into(), - ast::UintTy::U64 => u64::MAX.into(), - ast::UintTy::U128 => u128::MAX, + ty::UintTy::Usize => u64::MAX.into(), + ty::UintTy::U8 => u8::MAX.into(), + ty::UintTy::U16 => u16::MAX.into(), + ty::UintTy::U32 => u32::MAX.into(), + ty::UintTy::U64 => u64::MAX.into(), + ty::UintTy::U128 => u128::MAX, }; (0, max) } @@ -258,8 +258,8 @@ fn report_bin_hex_error( // // No suggestion for: `isize`, `usize`. fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> { - use rustc_ast::IntTy::*; - use rustc_ast::UintTy::*; + use ty::IntTy::*; + use ty::UintTy::*; macro_rules! find_fit { ($ty:expr, $val:expr, $negative:expr, $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => { @@ -302,7 +302,7 @@ fn lint_int_literal<'tcx>( type_limits: &TypeLimits, e: &'tcx hir::Expr<'tcx>, lit: &hir::Lit, - t: ast::IntTy, + t: ty::IntTy, v: u128, ) { let int_type = t.normalize(cx.sess().target.pointer_width); @@ -314,7 +314,14 @@ fn lint_int_literal<'tcx>( // avoiding use of -min to prevent overflow/panic if (negative && v > max + 1) || (!negative && v > max) { if let Some(repr_str) = get_bin_hex_repr(cx, lit) { - report_bin_hex_error(cx, e, attr::IntType::SignedInt(t), repr_str, v, negative); + report_bin_hex_error( + cx, + e, + attr::IntType::SignedInt(ty::ast_int_ty(t)), + repr_str, + v, + negative, + ); return; } @@ -351,7 +358,7 @@ fn lint_uint_literal<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, lit: &hir::Lit, - t: ast::UintTy, + t: ty::UintTy, ) { let uint_type = t.normalize(cx.sess().target.pointer_width); let (min, max) = uint_ty_range(uint_type); @@ -391,7 +398,14 @@ fn lint_uint_literal<'tcx>( } } if let Some(repr_str) = get_bin_hex_repr(cx, lit) { - report_bin_hex_error(cx, e, attr::IntType::UnsignedInt(t), repr_str, lit_val, false); + report_bin_hex_error( + cx, + e, + attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), + repr_str, + lit_val, + false, + ); return; } cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { @@ -430,8 +444,8 @@ fn lint_literal<'tcx>( ty::Float(t) => { let is_infinite = match lit.node { ast::LitKind::Float(v, _) => match t { - ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), - ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), + ty::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite), + ty::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite), }, _ => bug!(), }; @@ -647,8 +661,7 @@ pub fn transparent_newtype_field<'a, 'tcx>( let param_env = tcx.param_env(variant.def_id); for field in &variant.fields { let field_ty = tcx.type_of(field.did); - let is_zst = - tcx.layout_of(param_env.and(field_ty)).map(|layout| layout.is_zst()).unwrap_or(false); + let is_zst = tcx.layout_of(param_env.and(field_ty)).map_or(false, |layout| layout.is_zst()); if !is_zst { return Some(field); @@ -985,7 +998,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: Some("consider using `u32` or `libc::wchar_t` instead".into()), }, - ty::Int(ast::IntTy::I128) | ty::Uint(ast::UintTy::U128) => FfiUnsafe { + ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => FfiUnsafe { ty, reason: "128-bit integers don't currently have a known stable ABI".into(), help: None, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 5e1f94c071..b611aebad0 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -202,8 +202,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let mut has_emitted = false; for &(predicate, _) in cx.tcx.explicit_item_bounds(def) { // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::PredicateAtom::Trait(ref poly_trait_predicate, _) = - predicate.skip_binders() + if let ty::PredicateKind::Trait(ref poly_trait_predicate, _) = + predicate.kind().skip_binder() { let def_id = poly_trait_predicate.trait_ref.def_id; let descr_pre = @@ -529,8 +529,8 @@ trait UnusedDelimLint { pprust::expr_to_string(value) }; let keep_space = ( - left_pos.map(|s| s >= value.span.lo()).unwrap_or(false), - right_pos.map(|s| s <= value.span.hi()).unwrap_or(false), + left_pos.map_or(false, |s| s >= value.span.lo()), + right_pos.map_or(false, |s| s <= value.span.hi()), ); self.emit_unused_delims(cx, value.span, &expr_text, ctx.into(), keep_space); } @@ -862,11 +862,11 @@ impl EarlyLintPass for UnusedParens { } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { - if let &ast::TyKind::Paren(ref r) = &ty.kind { + if let ast::TyKind::Paren(r) = &ty.kind { match &r.kind { - &ast::TyKind::TraitObject(..) => {} - &ast::TyKind::ImplTrait(_, ref bounds) if bounds.len() > 1 => {} - &ast::TyKind::Array(_, ref len) => { + ast::TyKind::TraitObject(..) => {} + ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} + ast::TyKind::Array(_, len) => { self.check_unused_delims_expr( cx, &len.value, @@ -977,8 +977,6 @@ impl UnusedDelimLint for UnusedBraces { } } ast::ExprKind::Let(_, ref expr) => { - // FIXME(#60336): Properly handle `let true = (false && true)` - // actually needing the parenthesis. self.check_unused_delims_expr( cx, expr, diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml index 7f908088cf..f909f15978 100644 --- a/compiler/rustc_lint_defs/Cargo.toml +++ b/compiler/rustc_lint_defs/Cargo.toml @@ -11,3 +11,4 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_span = { path = "../rustc_span" } rustc_serialize = { path = "../rustc_serialize" } rustc_macros = { path = "../rustc_macros" } +rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 655eb229e6..da62ad3a6b 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1,10 +1,13 @@ +// ignore-tidy-filelength //! Some lints that are built in to the compiler. //! //! These are the built-in lints that are emitted direct in the main //! compiler code, rather than using their own custom pass. Those //! lints are all available in `rustc_lint::builtin`. -use crate::{declare_lint, declare_lint_pass, declare_tool_lint}; +// ignore-tidy-filelength + +use crate::{declare_lint, declare_lint_pass}; use rustc_span::edition::Edition; use rustc_span::symbol::sym; @@ -257,14 +260,10 @@ declare_lint! { /// /// ### Explanation /// - /// This lint detects code that is very likely incorrect. When possible, - /// the compiler will attempt to detect situations where code can be - /// evaluated at compile-time to generate more efficient code. While - /// evaluating such code, if it detects that the code will unconditionally - /// panic, this usually indicates that it is doing something incorrectly. - /// If this lint is allowed, then the code will not be evaluated at - /// compile-time, and instead continue to generate code to evaluate at - /// runtime, which may panic during runtime. + /// This lint detects code that is very likely incorrect because it will + /// always panic, such as division by zero and out-of-bounds array + /// accesses. Consider adjusting your code if this is a bug, or using the + /// `panic!` or `unreachable!` macro instead in case the panic is intended. pub UNCONDITIONAL_PANIC, Deny, "operation will cause a panic at runtime" @@ -278,41 +277,26 @@ declare_lint! { /// /// ```rust,compile_fail /// #![allow(unconditional_panic)] - /// let x: &'static i32 = &(1 / 0); + /// const C: i32 = 1/0; /// ``` /// /// {{produces}} /// /// ### Explanation /// - /// This lint detects code that is very likely incorrect. If this lint is - /// allowed, then the code will not be evaluated at compile-time, and - /// instead continue to generate code to evaluate at runtime, which may - /// panic during runtime. - /// - /// Note that this lint may trigger in either inside or outside of a - /// [const context]. Outside of a [const context], the compiler can - /// sometimes evaluate an expression at compile-time in order to generate - /// more efficient code. As the compiler becomes better at doing this, it - /// needs to decide what to do when it encounters code that it knows for - /// certain will panic or is otherwise incorrect. Making this a hard error - /// would prevent existing code that exhibited this behavior from - /// compiling, breaking backwards-compatibility. However, this is almost - /// certainly incorrect code, so this is a deny-by-default lint. For more - /// details, see [RFC 1229] and [issue #28238]. - /// - /// Note that there are several other more specific lints associated with - /// compile-time evaluation, such as [`arithmetic_overflow`], - /// [`unconditional_panic`]. + /// This lint detects constants that fail to evaluate. Allowing the lint will accept the + /// constant declaration, but any use of this constant will still lead to a hard error. This is + /// a future incompatibility lint; the plan is to eventually entirely forbid even declaring + /// constants that cannot be evaluated. See [issue #71800] for more details. /// - /// [const context]: https://doc.rust-lang.org/reference/const_eval.html#const-context - /// [RFC 1229]: https://github.com/rust-lang/rfcs/blob/master/text/1229-compile-time-asserts.md - /// [issue #28238]: https://github.com/rust-lang/rust/issues/28238 - /// [`arithmetic_overflow`]: deny-by-default.html#arithmetic-overflow - /// [`unconditional_panic`]: deny-by-default.html#unconditional-panic + /// [issue #71800]: https://github.com/rust-lang/rust/issues/71800 pub CONST_ERR, Deny, - "constant evaluation detected erroneous expression", + "constant evaluation encountered erroneous expression", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #71800 ", + edition: None, + }; report_in_external_macro } @@ -2865,12 +2849,79 @@ declare_lint! { }; } -declare_tool_lint! { - pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL, +declare_lint! { + /// The `ineffective_unstable_trait_impl` lint detects `#[unstable]` attributes which are not used. + /// + /// ### Example + /// + /// ```compile_fail + /// #![feature(staged_api)] + /// + /// #[derive(Clone)] + /// #[stable(feature = "x", since = "1")] + /// struct S {} + /// + /// #[unstable(feature = "y", issue = "none")] + /// impl Copy for S {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `staged_api` does not currently support using a stability attribute on `impl` blocks. + /// `impl`s are always stable if both the type and trait are stable, and always unstable otherwise. + pub INEFFECTIVE_UNSTABLE_TRAIT_IMPL, Deny, "detects `#[unstable]` on stable trait implementations for stable types" } +declare_lint! { + /// The `semicolon_in_expressions_from_macros` lint detects trailing semicolons + /// in macro bodies when the macro is invoked in expression position. + /// This was previous accepted, but is being phased out. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(semicolon_in_expressions_from_macros)] + /// macro_rules! foo { + /// () => { true; } + /// } + /// + /// fn main() { + /// let val = match true { + /// true => false, + /// _ => foo!() + /// }; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous, Rust ignored trailing semicolon in a macro + /// body when a macro was invoked in expression position. + /// However, this makes the treatment of semicolons in the language + /// inconsistent, and could lead to unexpected runtime behavior + /// in some circumstances (e.g. if the macro author expects + /// a value to be dropped). + /// + /// This is a [future-incompatible] lint to transition this + /// to a hard error in the future. See [issue #79813] for more details. + /// + /// [issue #79813]: https://github.com/rust-lang/rust/issues/79813 + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, + Allow, + "trailing semicolon in macro body used as expression", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #79813 ", + edition: None, + }; +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -2958,6 +3009,9 @@ declare_lint_pass! { FUNCTION_ITEM_REFERENCES, USELESS_DEPRECATED, UNSUPPORTED_NAKED_FUNCTIONS, + MISSING_ABI, + SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, + DISJOINT_CAPTURE_DROP_REORDER, ] } @@ -2984,4 +3038,74 @@ declare_lint! { "detects doc comments that aren't used by rustdoc" } +declare_lint! { + /// The `disjoint_capture_drop_reorder` lint detects variables that aren't completely + /// captured when the feature `capture_disjoint_fields` is enabled and it affects the Drop + /// order of at least one path starting at this variable. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// # #![deny(disjoint_capture_drop_reorder)] + /// # #![allow(unused)] + /// struct FancyInteger(i32); + /// + /// impl Drop for FancyInteger { + /// fn drop(&mut self) { + /// println!("Just dropped {}", self.0); + /// } + /// } + /// + /// struct Point { x: FancyInteger, y: FancyInteger } + /// + /// fn main() { + /// let p = Point { x: FancyInteger(10), y: FancyInteger(20) }; + /// + /// let c = || { + /// let x = p.x; + /// }; + /// + /// c(); + /// + /// // ... More code ... + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In the above example `p.y` will be dropped at the end of `f` instead of with `c` if + /// the feature `capture_disjoint_fields` is enabled. + pub DISJOINT_CAPTURE_DROP_REORDER, + Allow, + "Drop reorder because of `capture_disjoint_fields`" + +} + declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]); + +declare_lint! { + /// The `missing_abi` lint detects cases where the ABI is omitted from + /// extern declarations. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(missing_abi)] + /// + /// extern fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Historically, Rust implicitly selected C as the ABI for extern + /// declarations. We expect to add new ABIs, like `C-unwind`, in the future, + /// though this has not yet happened, and especially with their addition + /// seeing the ABI easily will make code review easier. + pub MISSING_ABI, + Allow, + "No declared ABI for extern declaration" +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index aec0fc253c..9d60a51a0a 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -6,6 +6,7 @@ use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_span::edition::Edition; use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol}; +use rustc_target::spec::abi::Abi; pub mod builtin; @@ -252,7 +253,9 @@ pub enum BuiltinLintDiagnostics { UnusedImports(String, Vec<(Span, String)>), RedundantImport(Vec<(Span, bool)>, Ident), DeprecatedMacro(Option, Span), + MissingAbi(Span, Abi), UnusedDocComment(Span), + PatternsInFnsWithoutBody(Span, Ident), } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index c0ff62c17b..4118e93074 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -216,6 +216,14 @@ extern "C" void LLVMRustAddCallSiteAttribute(LLVMValueRef Instr, unsigned Index, Call->addAttribute(Index, Attr); } +extern "C" void LLVMRustAddCallSiteAttrString(LLVMValueRef Instr, unsigned Index, + const char *Name) { + CallBase *Call = unwrap(Instr); + Attribute Attr = Attribute::get(Call->getContext(), Name); + Call->addAttribute(Index, Attr); +} + + extern "C" void LLVMRustAddAlignmentCallSiteAttr(LLVMValueRef Instr, unsigned Index, uint32_t Bytes) { @@ -660,6 +668,8 @@ extern "C" uint32_t LLVMRustDebugMetadataVersion() { return DEBUG_METADATA_VERSION; } +extern "C" uint32_t LLVMRustVersionPatch() { return LLVM_VERSION_PATCH; } + extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; } extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; } @@ -994,11 +1004,9 @@ LLVMRustDICompositeTypeReplaceArrays(LLVMRustDIBuilderRef Builder, } extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateDebugLocation(LLVMContextRef ContextRef, unsigned Line, - unsigned Column, LLVMMetadataRef Scope, +LLVMRustDIBuilderCreateDebugLocation(unsigned Line, unsigned Column, + LLVMMetadataRef Scope, LLVMMetadataRef InlinedAt) { - LLVMContext &Context = *unwrap(ContextRef); - DebugLoc debug_loc = DebugLoc::get(Line, Column, unwrapDIPtr(Scope), unwrapDIPtr(InlinedAt)); diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index b2ffa271f6..592010d78c 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -38,7 +38,7 @@ pub fn initialize_available_targets() { ($cfg:meta, $($method:ident),*) => { { #[cfg($cfg)] fn init() { - extern { + extern "C" { $(fn $method();)* } unsafe { diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 12990ae2d9..cff8e98331 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -5,8 +5,8 @@ use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ - braced, parenthesized, parse_macro_input, AttrStyle, Attribute, Block, Error, Expr, Ident, - ReturnType, Token, Type, + braced, parenthesized, parse_macro_input, parse_quote, AttrStyle, Attribute, Block, Error, + Expr, Ident, ReturnType, Token, Type, }; mod kw { @@ -189,25 +189,6 @@ impl Parse for List { } } -/// A named group containing queries. -/// -/// For now, the name is not used any more, but the capability remains interesting for future -/// developments of the query system. -struct Group { - #[allow(unused)] - name: Ident, - queries: List, -} - -impl Parse for Group { - fn parse(input: ParseStream<'_>) -> Result { - let name: Ident = input.parse()?; - let content; - braced!(content in input); - Ok(Group { name, queries: content.parse()? }) - } -} - struct QueryModifiers { /// The description of the query. desc: (Option, Punctuated), @@ -272,6 +253,40 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers { if desc.is_some() { panic!("duplicate modifier `desc` for query `{}`", query.name); } + // If there are no doc-comments, give at least some idea of what + // it does by showing the query description. + if query.doc_comments.is_empty() { + use ::syn::*; + let mut list = list.iter(); + let format_str: String = match list.next() { + Some(&Expr::Lit(ExprLit { lit: Lit::Str(ref lit_str), .. })) => { + lit_str.value().replace("`{}`", "{}") // We add them later anyways for consistency + } + _ => panic!("Expected a string literal"), + }; + let mut fmt_fragments = format_str.split("{}"); + let mut doc_string = fmt_fragments.next().unwrap().to_string(); + list.map(::quote::ToTokens::to_token_stream).zip(fmt_fragments).for_each( + |(tts, next_fmt_fragment)| { + use ::core::fmt::Write; + write!( + &mut doc_string, + " `{}` {}", + tts.to_string().replace(" . ", "."), + next_fmt_fragment, + ) + .unwrap(); + }, + ); + let doc_string = format!( + "[query description - consider adding a doc-comment!] {}", + doc_string + ); + let comment = parse_quote! { + #[doc = #doc_string] + }; + query.doc_comments.push(comment); + } desc = Some((tcx, list)); } QueryModifier::FatalCycle => { @@ -395,7 +410,7 @@ fn add_query_description_impl( }; let (tcx, desc) = modifiers.desc; - let tcx = tcx.as_ref().map(|t| quote! { #t }).unwrap_or(quote! { _ }); + let tcx = tcx.as_ref().map_or(quote! { _ }, |t| quote! { #t }); let desc = quote! { #[allow(unused_variables)] @@ -416,72 +431,70 @@ fn add_query_description_impl( } pub fn rustc_queries(input: TokenStream) -> TokenStream { - let groups = parse_macro_input!(input as List); + let queries = parse_macro_input!(input as List); let mut query_stream = quote! {}; let mut query_description_stream = quote! {}; let mut dep_node_def_stream = quote! {}; let mut cached_queries = quote! {}; - for group in groups.0 { - for mut query in group.queries.0 { - let modifiers = process_modifiers(&mut query); - let name = &query.name; - let arg = &query.arg; - let result_full = &query.result; - let result = match query.result { - ReturnType::Default => quote! { -> () }, - _ => quote! { #result_full }, - }; + for mut query in queries.0 { + let modifiers = process_modifiers(&mut query); + let name = &query.name; + let arg = &query.arg; + let result_full = &query.result; + let result = match query.result { + ReturnType::Default => quote! { -> () }, + _ => quote! { #result_full }, + }; - if modifiers.cache.is_some() { - cached_queries.extend(quote! { - #name, - }); - } + if modifiers.cache.is_some() { + cached_queries.extend(quote! { + #name, + }); + } - let mut attributes = Vec::new(); + let mut attributes = Vec::new(); - // Pass on the fatal_cycle modifier - if modifiers.fatal_cycle { - attributes.push(quote! { fatal_cycle }); - }; - // Pass on the storage modifier - if let Some(ref ty) = modifiers.storage { - attributes.push(quote! { storage(#ty) }); - }; - // Pass on the cycle_delay_bug modifier - if modifiers.cycle_delay_bug { - attributes.push(quote! { cycle_delay_bug }); - }; - // Pass on the no_hash modifier - if modifiers.no_hash { - attributes.push(quote! { no_hash }); - }; - // Pass on the anon modifier - if modifiers.anon { - attributes.push(quote! { anon }); - }; - // Pass on the eval_always modifier - if modifiers.eval_always { - attributes.push(quote! { eval_always }); - }; + // Pass on the fatal_cycle modifier + if modifiers.fatal_cycle { + attributes.push(quote! { fatal_cycle }); + }; + // Pass on the storage modifier + if let Some(ref ty) = modifiers.storage { + attributes.push(quote! { storage(#ty) }); + }; + // Pass on the cycle_delay_bug modifier + if modifiers.cycle_delay_bug { + attributes.push(quote! { cycle_delay_bug }); + }; + // Pass on the no_hash modifier + if modifiers.no_hash { + attributes.push(quote! { no_hash }); + }; + // Pass on the anon modifier + if modifiers.anon { + attributes.push(quote! { anon }); + }; + // Pass on the eval_always modifier + if modifiers.eval_always { + attributes.push(quote! { eval_always }); + }; - let attribute_stream = quote! {#(#attributes),*}; - let doc_comments = query.doc_comments.iter(); - // Add the query to the group - query_stream.extend(quote! { - #(#doc_comments)* - [#attribute_stream] fn #name(#arg) #result, - }); + let attribute_stream = quote! {#(#attributes),*}; + let doc_comments = query.doc_comments.iter(); + // Add the query to the group + query_stream.extend(quote! { + #(#doc_comments)* + [#attribute_stream] fn #name(#arg) #result, + }); - // Create a dep node for the query - dep_node_def_stream.extend(quote! { - [#attribute_stream] #name(#arg), - }); + // Create a dep node for the query + dep_node_def_stream.extend(quote! { + [#attribute_stream] #name(#arg), + }); - add_query_description_impl(&query, modifiers, &mut query_description_stream); - } + add_query_description_impl(&query, modifiers, &mut query_description_stream); } TokenStream::from(quote! { diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index dbeb3c7550..72bd4804e9 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -203,7 +203,7 @@ fn encodable_body( #field_name, #field_idx, |__encoder| - ::rustc_serialize::Encodable::encode(#bind_ident, __encoder), + ::rustc_serialize::Encodable::<#encoder_ty>::encode(#bind_ident, __encoder), ) { ::std::result::Result::Ok(()) => (), ::std::result::Result::Err(__err) @@ -237,7 +237,7 @@ fn encodable_body( __encoder, #field_idx, |__encoder| - ::rustc_serialize::Encodable::encode(#bind_ident, __encoder), + ::rustc_serialize::Encodable::<#encoder_ty>::encode(#bind_ident, __encoder), ) { ::std::result::Result::Ok(()) => (), ::std::result::Result::Err(__err) diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs index 610b9155cf..5c061a9d3c 100644 --- a/compiler/rustc_macros/src/session_diagnostic.rs +++ b/compiler/rustc_macros/src/session_diagnostic.rs @@ -574,7 +574,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> { /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y) /// ``` /// This function builds the entire call to format!. - fn build_format(&self, input: &String, span: proc_macro2::Span) -> proc_macro2::TokenStream { + fn build_format(&self, input: &str, span: proc_macro2::Span) -> proc_macro2::TokenStream { // This set is used later to generate the final format string. To keep builds reproducible, // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead // of a HashSet. diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 019ca5174a..e3fbd1a2b2 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -28,7 +28,7 @@ use rustc_target::spec::{PanicStrategy, TargetTriple}; use proc_macro::bridge::client::ProcMacro; use std::path::Path; -use std::{cmp, env, fs}; +use std::{cmp, env}; use tracing::{debug, info}; #[derive(Clone)] @@ -252,9 +252,10 @@ impl<'a> CrateLoader<'a> { // Only use `--extern crate_name=path` here, not `--extern crate_name`. if let Some(mut files) = entry.files() { if files.any(|l| { - let l = fs::canonicalize(l).unwrap_or(l.clone().into()); - source.dylib.as_ref().map(|p| &p.0) == Some(&l) - || source.rlib.as_ref().map(|p| &p.0) == Some(&l) + let l = l.canonicalized(); + source.dylib.as_ref().map(|(p, _)| p) == Some(l) + || source.rlib.as_ref().map(|(p, _)| p) == Some(l) + || source.rmeta.as_ref().map(|(p, _)| p) == Some(l) }) { ret = Some(cnum); } @@ -326,7 +327,7 @@ impl<'a> CrateLoader<'a> { self.verify_no_symbol_conflicts(&crate_root)?; let private_dep = - self.sess.opts.externs.get(&name.as_str()).map(|e| e.is_private_dep).unwrap_or(false); + self.sess.opts.externs.get(&name.as_str()).map_or(false, |e| e.is_private_dep); // Claim this crate number and cache it let cnum = self.cstore.alloc_new_crate_num(); diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index c4c025de8b..b66c6cffb1 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -224,6 +224,7 @@ use rustc_middle::middle::cstore::{CrateSource, MetadataLoader}; use rustc_session::config::{self, CrateType}; use rustc_session::filesearch::{FileDoesntMatch, FileMatches, FileSearch}; use rustc_session::search_paths::PathKind; +use rustc_session::utils::CanonicalizedPath; use rustc_session::{CrateDisambiguator, Session}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; @@ -244,7 +245,7 @@ crate struct CrateLocator<'a> { // Immutable per-search configuration. crate_name: Symbol, - exact_paths: Vec, + exact_paths: Vec, pub hash: Option, pub host_hash: Option, extra_filename: Option<&'a str>, @@ -315,7 +316,7 @@ impl<'a> CrateLocator<'a> { .into_iter() .filter_map(|entry| entry.files()) .flatten() - .map(PathBuf::from) + .cloned() .collect() } else { // SVH being specified means this is a transitive dependency, @@ -664,13 +665,19 @@ impl<'a> CrateLocator<'a> { let mut rmetas = FxHashMap::default(); let mut dylibs = FxHashMap::default(); for loc in &self.exact_paths { - if !loc.exists() { - return Err(CrateError::ExternLocationNotExist(self.crate_name, loc.clone())); + if !loc.canonicalized().exists() { + return Err(CrateError::ExternLocationNotExist( + self.crate_name, + loc.original().clone(), + )); } - let file = match loc.file_name().and_then(|s| s.to_str()) { + let file = match loc.original().file_name().and_then(|s| s.to_str()) { Some(file) => file, None => { - return Err(CrateError::ExternLocationNotFile(self.crate_name, loc.clone())); + return Err(CrateError::ExternLocationNotFile( + self.crate_name, + loc.original().clone(), + )); } }; @@ -685,7 +692,8 @@ impl<'a> CrateLocator<'a> { // e.g. symbolic links. If we canonicalize too early, we resolve // the symlink, the file type is lost and we might treat rlibs and // rmetas as dylibs. - let loc_canon = fs::canonicalize(&loc).unwrap_or_else(|_| loc.clone()); + let loc_canon = loc.canonicalized().clone(); + let loc = loc.original(); if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") { rlibs.insert(loc_canon, PathKind::ExternFlag); } else if loc.file_name().unwrap().to_str().unwrap().ends_with(".rmeta") { @@ -695,7 +703,7 @@ impl<'a> CrateLocator<'a> { } } else { self.rejected_via_filename - .push(CrateMismatch { path: loc.clone(), got: String::new() }); + .push(CrateMismatch { path: loc.original().clone(), got: String::new() }); } } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index fe29f9d177..8d0994320e 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -132,7 +132,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { impl Collector<'tcx> { fn register_native_lib(&mut self, span: Option, lib: NativeLib) { - if lib.name.as_ref().map(|&s| s == kw::Invalid).unwrap_or(false) { + if lib.name.as_ref().map_or(false, |&s| s == kw::Empty) { match span { Some(span) => { struct_span_err!( @@ -192,13 +192,13 @@ impl Collector<'tcx> { fn process_command_line(&mut self) { // First, check for errors let mut renames = FxHashSet::default(); - for &(ref name, ref new_name, _) in &self.tcx.sess.opts.libs { - if let &Some(ref new_name) = new_name { + for (name, new_name, _) in &self.tcx.sess.opts.libs { + if let Some(ref new_name) = new_name { let any_duplicate = self .libs .iter() .filter_map(|lib| lib.name.as_ref()) - .any(|n| n.as_str() == *name); + .any(|n| &n.as_str() == name); if new_name.is_empty() { self.tcx.sess.err(&format!( "an empty renaming target was specified for library `{}`", @@ -240,7 +240,7 @@ impl Collector<'tcx> { if kind != NativeLibKind::Unspecified { lib.kind = kind; } - if let &Some(ref new_name) = new_name { + if let Some(new_name) = new_name { lib.name = Some(Symbol::intern(new_name)); } return true; diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 43f7b2a992..e3c3539079 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -10,7 +10,8 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::{AtomicCell, Lock, LockGuard, Lrc, OnceCell}; +use rustc_data_structures::sync::{Lock, LockGuard, Lrc, OnceCell}; +use rustc_data_structures::unhash::UnhashMap; use rustc_errors::ErrorReported; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, ProcMacroDerive}; @@ -20,7 +21,6 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE} use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash}; use rustc_hir::lang_items; use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::dep_graph::{self, DepNode, DepNodeExt, DepNodeIndex}; use rustc_middle::hir::exports::Export; use rustc_middle::middle::cstore::{CrateSource, ExternCrate}; use rustc_middle::middle::cstore::{ForeignModule, LinkagePreference, NativeLib}; @@ -80,14 +80,9 @@ crate struct CrateMetadata { /// For every definition in this crate, maps its `DefPathHash` to its /// `DefIndex`. See `raw_def_id_to_def_id` for more details about how /// this is used. - def_path_hash_map: OnceCell>, + def_path_hash_map: OnceCell>, /// Used for decoding interpret::AllocIds in a cached & thread-safe manner. alloc_decoding_state: AllocDecodingState, - /// The `DepNodeIndex` of the `DepNode` representing this upstream crate. - /// It is initialized on the first access in `get_crate_dep_node_index()`. - /// Do not access the value directly, as it might not have been initialized yet. - /// The field must always be initialized to `DepNodeIndex::INVALID`. - dep_node_index: AtomicCell, /// Caches decoded `DefKey`s. def_key_cache: Lock>, /// Caches decoded `DefPathHash`es. @@ -623,43 +618,6 @@ impl MetadataBlob { } } -impl EntryKind { - fn def_kind(&self) -> DefKind { - match *self { - EntryKind::AnonConst(..) => DefKind::AnonConst, - EntryKind::Const(..) => DefKind::Const, - EntryKind::AssocConst(..) => DefKind::AssocConst, - EntryKind::ImmStatic - | EntryKind::MutStatic - | EntryKind::ForeignImmStatic - | EntryKind::ForeignMutStatic => DefKind::Static, - EntryKind::Struct(_, _) => DefKind::Struct, - EntryKind::Union(_, _) => DefKind::Union, - EntryKind::Fn(_) | EntryKind::ForeignFn(_) => DefKind::Fn, - EntryKind::AssocFn(_) => DefKind::AssocFn, - EntryKind::Type => DefKind::TyAlias, - EntryKind::TypeParam => DefKind::TyParam, - EntryKind::ConstParam => DefKind::ConstParam, - EntryKind::OpaqueTy => DefKind::OpaqueTy, - EntryKind::AssocType(_) => DefKind::AssocTy, - EntryKind::Mod(_) => DefKind::Mod, - EntryKind::Variant(_) => DefKind::Variant, - EntryKind::Trait(_) => DefKind::Trait, - EntryKind::TraitAlias => DefKind::TraitAlias, - EntryKind::Enum(..) => DefKind::Enum, - EntryKind::MacroDef(_) => DefKind::Macro(MacroKind::Bang), - EntryKind::ProcMacro(kind) => DefKind::Macro(kind), - EntryKind::ForeignType => DefKind::ForeignTy, - EntryKind::Impl(_) => DefKind::Impl, - EntryKind::Closure => DefKind::Closure, - EntryKind::ForeignMod => DefKind::ForeignMod, - EntryKind::GlobalAsm => DefKind::GlobalAsm, - EntryKind::Field => DefKind::Field, - EntryKind::Generator(_) => DefKind::Generator, - } - } -} - impl CrateRoot<'_> { crate fn is_proc_macro_crate(&self) -> bool { self.proc_macro_data.is_some() @@ -690,21 +648,6 @@ impl CrateRoot<'_> { } impl<'a, 'tcx> CrateMetadataRef<'a> { - fn maybe_kind(&self, item_id: DefIndex) -> Option { - self.root.tables.kind.get(self, item_id).map(|k| k.decode(self)) - } - - fn kind(&self, item_id: DefIndex) -> EntryKind { - self.maybe_kind(item_id).unwrap_or_else(|| { - bug!( - "CrateMetadata::kind({:?}): id not found, in crate {:?} with number {}", - item_id, - self.root.name, - self.cnum, - ) - }) - } - fn raw_proc_macro(&self, id: DefIndex) -> &ProcMacro { // DefIndex's in root.proc_macro_data have a one-to-one correspondence // with items in 'raw_proc_macros'. @@ -720,25 +663,51 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { &self.raw_proc_macros.unwrap()[pos] } - fn item_ident(&self, item_index: DefIndex, sess: &Session) -> Ident { + fn try_item_ident(&self, item_index: DefIndex, sess: &Session) -> Result { let name = self .def_key(item_index) .disambiguated_data .data .get_opt_name() - .expect("no name in item_ident"); + .ok_or_else(|| format!("Missing opt name for {:?}", item_index))?; let span = self .root .tables .ident_span .get(self, item_index) - .map(|data| data.decode((self, sess))) - .unwrap_or_else(|| panic!("Missing ident span for {:?} ({:?})", name, item_index)); - Ident::new(name, span) + .ok_or_else(|| format!("Missing ident span for {:?} ({:?})", name, item_index))? + .decode((self, sess)); + Ok(Ident::new(name, span)) } - fn def_kind(&self, index: DefIndex) -> DefKind { - self.kind(index).def_kind() + fn item_ident(&self, item_index: DefIndex, sess: &Session) -> Ident { + self.try_item_ident(item_index, sess).unwrap() + } + + fn maybe_kind(&self, item_id: DefIndex) -> Option { + self.root.tables.kind.get(self, item_id).map(|k| k.decode(self)) + } + + fn kind(&self, item_id: DefIndex) -> EntryKind { + self.maybe_kind(item_id).unwrap_or_else(|| { + bug!( + "CrateMetadata::kind({:?}): id not found, in crate {:?} with number {}", + item_id, + self.root.name, + self.cnum, + ) + }) + } + + fn def_kind(&self, item_id: DefIndex) -> DefKind { + self.root.tables.def_kind.get(self, item_id).map(|k| k.decode(self)).unwrap_or_else(|| { + bug!( + "CrateMetadata::def_kind({:?}): id not found, in crate {:?} with number {}", + item_id, + self.root.name, + self.cnum, + ) + }) } fn get_span(&self, index: DefIndex, sess: &Session) -> Span { @@ -1055,19 +1024,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // Iterate over all children. let macros_only = self.dep_kind.lock().macros_only(); - let children = self.root.tables.children.get(self, id).unwrap_or_else(Lazy::empty); - for child_index in children.decode((self, sess)) { - if macros_only { - continue; - } - - // Get the item. - if let Some(child_kind) = self.maybe_kind(child_index) { - match child_kind { - EntryKind::MacroDef(..) => {} - _ if macros_only => continue, - _ => {} - } + if !macros_only { + let children = self.root.tables.children.get(self, id).unwrap_or_else(Lazy::empty); + + for child_index in children.decode((self, sess)) { + // Get the item. + let child_kind = match self.maybe_kind(child_index) { + Some(child_kind) => child_kind, + None => continue, + }; // Hand off the item to the callback. match child_kind { @@ -1102,8 +1067,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } let def_key = self.def_key(child_index); - let span = self.get_span(child_index, sess); if def_key.disambiguated_data.data.get_opt_name().is_some() { + let span = self.get_span(child_index, sess); let kind = self.def_kind(child_index); let ident = self.item_ident(child_index, sess); let vis = self.get_visibility(child_index); @@ -1137,9 +1102,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // within the crate. We only need this for fictive constructors, // for other constructors correct visibilities // were already encoded in metadata. - let attrs: Vec<_> = - self.get_item_attrs(def_id.index, sess).collect(); - if sess.contains_name(&attrs, sym::non_exhaustive) { + let mut attrs = self.get_item_attrs(def_id.index, sess); + if attrs.any(|item| item.has_name(sym::non_exhaustive)) { let crate_def_id = self.local_def_id(CRATE_DEF_INDEX); vis = ty::Visibility::Restricted(crate_def_id); } @@ -1164,6 +1128,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } + fn is_ctfe_mir_available(&self, id: DefIndex) -> bool { + self.root.tables.mir_for_ctfe.get(self, id).is_some() + } + fn is_item_mir_available(&self, id: DefIndex) -> bool { self.root.tables.mir.get(self, id).is_some() } @@ -1187,6 +1155,17 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .decode((self, tcx)) } + fn get_mir_for_ctfe(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> { + self.root + .tables + .mir_for_ctfe + .get(self, id) + .unwrap_or_else(|| { + bug!("get_mir_for_ctfe: missing MIR for `{:?}`", self.local_def_id(id)) + }) + .decode((self, tcx)) + } + fn get_mir_abstract_const( &self, tcx: TyCtxt<'tcx>, @@ -1345,15 +1324,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { return &[]; } - // Do a reverse lookup beforehand to avoid touching the crate_num - // hash map in the loop below. - let filter = match filter.map(|def_id| self.reverse_translate_def_id(def_id)) { - Some(Some(def_id)) => Some((def_id.krate.as_u32(), def_id.index)), - Some(None) => return &[], - None => None, - }; + if let Some(def_id) = filter { + // Do a reverse lookup beforehand to avoid touching the crate_num + // hash map in the loop below. + let filter = match self.reverse_translate_def_id(def_id) { + Some(def_id) => (def_id.krate.as_u32(), def_id.index), + None => return &[], + }; - if let Some(filter) = filter { if let Some(impls) = self.trait_impls.get(&filter) { tcx.arena.alloc_from_iter( impls.decode(self).map(|(idx, simplified_self_ty)| { @@ -1560,7 +1538,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // stored in this crate. let map = self.cdata.def_path_hash_map.get_or_init(|| { let end_id = self.root.tables.def_path_hashes.size() as u32; - let mut map = FxHashMap::with_capacity_and_hasher(end_id as usize, Default::default()); + let mut map = UnhashMap::with_capacity_and_hasher(end_id as usize, Default::default()); for i in 0..end_id { let def_index = DefIndex::from_u32(i); // There may be gaps in the encoded table if we're decoding a proc-macro crate @@ -1597,31 +1575,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.def_path_hash_unlocked(index, &mut def_path_hashes) } - /// Get the `DepNodeIndex` corresponding this crate. The result of this - /// method is cached in the `dep_node_index` field. - fn get_crate_dep_node_index(&self, tcx: TyCtxt<'tcx>) -> DepNodeIndex { - let mut dep_node_index = self.dep_node_index.load(); - - if unlikely!(dep_node_index == DepNodeIndex::INVALID) { - // We have not cached the DepNodeIndex for this upstream crate yet, - // so use the dep-graph to find it out and cache it. - // Note that multiple threads can enter this block concurrently. - // That is fine because the DepNodeIndex remains constant - // throughout the whole compilation session, and multiple stores - // would always write the same value. - - let def_path_hash = self.def_path_hash(CRATE_DEF_INDEX); - let dep_node = - DepNode::from_def_path_hash(def_path_hash, dep_graph::DepKind::CrateMetadata); - - dep_node_index = tcx.dep_graph.dep_node_index_of(&dep_node); - assert!(dep_node_index != DepNodeIndex::INVALID); - self.dep_node_index.store(dep_node_index); - } - - dep_node_index - } - /// Imports the source_map from an external crate into the source_map of the crate /// currently being compiled (the "local crate"). /// @@ -1838,7 +1791,6 @@ impl CrateMetadata { source_map_import_info: OnceCell::new(), def_path_hash_map: Default::default(), alloc_decoding_state, - dep_node_index: AtomicCell::new(DepNodeIndex::INVALID), cnum, cnum_map, dependencies, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index b7f2288521..828c025d38 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -44,13 +44,16 @@ macro_rules! provide { let ($def_id, $other) = def_id_arg.into_args(); assert!(!$def_id.is_local()); - let $cdata = CStore::from_tcx($tcx).get_crate_data($def_id.krate); - - if $tcx.dep_graph.is_fully_enabled() { - let crate_dep_node_index = $cdata.get_crate_dep_node_index($tcx); - $tcx.dep_graph.read_index(crate_dep_node_index); + // External query providers call `crate_hash` in order to register a dependency + // on the crate metadata. The exception is `crate_hash` itself, which obviously + // doesn't need to do this (and can't, as it would cause a query cycle). + use rustc_middle::dep_graph::DepKind; + if DepKind::$name != DepKind::crate_hash && $tcx.dep_graph.is_fully_enabled() { + $tcx.ensure().crate_hash($def_id.krate); } + let $cdata = CStore::from_tcx($tcx).get_crate_data($def_id.krate); + $compute })* @@ -115,6 +118,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, }) } optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) } + mir_for_ctfe => { tcx.arena.alloc(cdata.get_mir_for_ctfe(tcx, def_id.index)) } promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) } mir_abstract_const => { cdata.get_mir_abstract_const(tcx, def_id.index) } unused_generic_params => { cdata.get_unused_generic_params(def_id.index) } @@ -126,8 +130,11 @@ provide! { <'tcx> tcx, def_id, other, cdata, is_foreign_item => { cdata.is_foreign_item(def_id.index) } static_mutability => { cdata.static_mutability(def_id.index) } generator_kind => { cdata.generator_kind(def_id.index) } - def_kind => { cdata.def_kind(def_id.index) } + opt_def_kind => { Some(cdata.def_kind(def_id.index)) } def_span => { cdata.get_span(def_id.index, &tcx.sess) } + def_ident_span => { + cdata.try_item_ident(def_id.index, &tcx.sess).ok().map(|ident| ident.span) + } lookup_stability => { cdata.get_stability(def_id.index).map(|s| tcx.intern_stability(s)) } @@ -145,6 +152,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, impl_parent => { cdata.get_parent_impl(def_id.index) } trait_of_item => { cdata.get_trait_of_item(def_id.index) } is_mir_available => { cdata.is_item_mir_available(def_id.index) } + is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) } dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) } is_panic_runtime => { cdata.root.panic_runtime } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 4dfe3e8487..dd6a6fe624 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1,17 +1,16 @@ use crate::rmeta::table::{FixedSizeEncoding, TableBuilder}; use crate::rmeta::*; -use rustc_ast as ast; use rustc_data_structures::fingerprint::{Fingerprint, FingerprintEncoder}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::stable_hasher::StableHasher; -use rustc_data_structures::sync::{join, Lrc}; +use rustc_data_structures::sync::{join, par_iter, Lrc, ParallelIterator}; use rustc_hir as hir; -use rustc_hir::def::CtorKind; +use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::DefPathData; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor}; +use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::lang_items; use rustc_hir::{AnonConst, GenericParamKind}; use rustc_index::bit_set::GrowableBitSet; @@ -46,7 +45,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { lazy_state: LazyState, type_shorthands: FxHashMap, usize>, - predicate_shorthands: FxHashMap, usize>, + predicate_shorthands: FxHashMap, usize>, interpret_allocs: FxIndexSet, @@ -308,7 +307,7 @@ impl<'a, 'tcx> Encodable> for Span { impl<'a, 'tcx> FingerprintEncoder for EncodeContext<'a, 'tcx> { fn encode_fingerprint(&mut self, f: &Fingerprint) -> Result<(), Self::Error> { - f.encode_opaque(&mut self.opaque) + self.opaque.encode_fingerprint(f) } } @@ -323,7 +322,7 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { &mut self.type_shorthands } - fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { &mut self.predicate_shorthands } @@ -432,7 +431,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_info_for_items(&mut self) { let krate = self.tcx.hir().krate(); - self.encode_info_for_mod(hir::CRATE_HIR_ID, &krate.item.module, &krate.item.attrs); + self.encode_info_for_mod(hir::CRATE_HIR_ID, &krate.item.module); // Proc-macro crates only export proc-macro items, which are looked // up using `proc_macro_data` @@ -575,6 +574,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Encode the items. i = self.position(); + self.encode_def_ids(); + self.encode_mir(); self.encode_info_for_items(); let item_bytes = self.position() - i; @@ -694,7 +695,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { println!(" lang item bytes: {}", lang_item_bytes); println!(" diagnostic item bytes: {}", diagnostic_item_bytes); println!(" native bytes: {}", native_lib_bytes); - println!(" source_map bytes: {}", source_map_bytes); + println!(" source_map bytes: {}", source_map_bytes); println!(" impl bytes: {}", impl_bytes); println!(" exp. symbols bytes: {}", exported_symbols_bytes); println!(" def-path table bytes: {}", def_path_table_bytes); @@ -710,7 +711,154 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } +fn should_encode_visibility(def_kind: DefKind) -> bool { + match def_kind { + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::Fn + | DefKind::Const + | DefKind::Static + | DefKind::Ctor(..) + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::Macro(..) + | DefKind::Use + | DefKind::ForeignMod + | DefKind::OpaqueTy + | DefKind::Impl + | DefKind::Field => true, + DefKind::TyParam + | DefKind::ConstParam + | DefKind::LifetimeParam + | DefKind::AnonConst + | DefKind::GlobalAsm + | DefKind::Closure + | DefKind::Generator + | DefKind::ExternCrate => false, + } +} + +fn should_encode_stability(def_kind: DefKind) -> bool { + match def_kind { + DefKind::Mod + | DefKind::Ctor(..) + | DefKind::Variant + | DefKind::Field + | DefKind::Struct + | DefKind::AssocTy + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Static + | DefKind::Const + | DefKind::Fn + | DefKind::ForeignMod + | DefKind::TyAlias + | DefKind::OpaqueTy + | DefKind::Enum + | DefKind::Union + | DefKind::Impl + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::Macro(..) + | DefKind::ForeignTy => true, + DefKind::Use + | DefKind::LifetimeParam + | DefKind::AnonConst + | DefKind::GlobalAsm + | DefKind::Closure + | DefKind::Generator + | DefKind::ExternCrate => false, + } +} + +/// Whether we should encode MIR. +/// +/// Computing, optimizing and encoding the MIR is a relatively expensive operation. +/// We want to avoid this work when not required. Therefore: +/// - we only compute `mir_for_ctfe` on items with const-eval semantics; +/// - we skip `optimized_mir` for check runs. +/// +/// Return a pair, resp. for CTFE and for LLVM. +fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) { + match tcx.def_kind(def_id) { + // Constructors + DefKind::Ctor(_, _) => { + let mir_opt_base = tcx.sess.opts.output_types.should_codegen() + || tcx.sess.opts.debugging_opts.always_encode_mir; + (true, mir_opt_base) + } + // Constants + DefKind::AnonConst | DefKind::AssocConst | DefKind::Static | DefKind::Const => { + (true, false) + } + // Full-fledged functions + DefKind::AssocFn | DefKind::Fn => { + let generics = tcx.generics_of(def_id); + let needs_inline = (generics.requires_monomorphization(tcx) + || tcx.codegen_fn_attrs(def_id).requests_inline()) + && tcx.sess.opts.output_types.should_codegen(); + // Only check the presence of the `const` modifier. + let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id()); + let always_encode_mir = tcx.sess.opts.debugging_opts.always_encode_mir; + (is_const_fn, needs_inline || always_encode_mir) + } + // Closures can't be const fn. + DefKind::Closure => { + let generics = tcx.generics_of(def_id); + let needs_inline = (generics.requires_monomorphization(tcx) + || tcx.codegen_fn_attrs(def_id).requests_inline()) + && tcx.sess.opts.output_types.should_codegen(); + let always_encode_mir = tcx.sess.opts.debugging_opts.always_encode_mir; + (false, needs_inline || always_encode_mir) + } + // Generators require optimized MIR to compute layout. + DefKind::Generator => (false, true), + // The others don't have MIR. + _ => (false, false), + } +} + impl EncodeContext<'a, 'tcx> { + fn encode_def_ids(&mut self) { + if self.is_proc_macro { + return; + } + let tcx = self.tcx; + let hir = tcx.hir(); + for local_id in hir.iter_local_def_id() { + let def_id = local_id.to_def_id(); + let def_kind = tcx.opt_def_kind(local_id); + let def_kind = if let Some(def_kind) = def_kind { def_kind } else { continue }; + record!(self.tables.def_kind[def_id] <- match def_kind { + // Replace Ctor by the enclosing object to avoid leaking details in children crates. + DefKind::Ctor(CtorOf::Struct, _) => DefKind::Struct, + DefKind::Ctor(CtorOf::Variant, _) => DefKind::Variant, + def_kind => def_kind, + }); + record!(self.tables.span[def_id] <- tcx.def_span(def_id)); + record!(self.tables.attributes[def_id] <- tcx.get_attrs(def_id)); + record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); + if should_encode_visibility(def_kind) { + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); + } + if should_encode_stability(def_kind) { + self.encode_stability(def_id); + self.encode_const_stability(def_id); + self.encode_deprecation(def_id); + } + } + } + fn encode_variances_of(&mut self, def_id: DefId) { debug!("EncodeContext::encode_variances_of({:?})", def_id); record!(self.tables.variances[def_id] <- &self.tcx.variances_of(def_id)[..]); @@ -735,17 +883,11 @@ impl EncodeContext<'a, 'tcx> { }; record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data))); - record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); - record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); - record!(self.tables.attributes[def_id] <- &self.tcx.get_attrs(def_id)[..]); - record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); record!(self.tables.children[def_id] <- variant.fields.iter().map(|f| { assert!(f.did.is_local()); f.did.index })); self.encode_ident_span(def_id, variant.ident); - self.encode_stability(def_id); - self.encode_deprecation(def_id); self.encode_item_type(def_id); if variant.ctor_kind == CtorKind::Fn { // FIXME(eddyb) encode signature only in `encode_enum_variant_ctor`. @@ -758,8 +900,6 @@ impl EncodeContext<'a, 'tcx> { self.encode_generics(def_id); self.encode_explicit_predicates(def_id); self.encode_inferred_outlives(def_id); - self.encode_optimized_mir(def_id.expect_local()); - self.encode_promoted_mir(def_id.expect_local()); } fn encode_enum_variant_ctor(&mut self, def: &ty::AdtDef, index: VariantIdx) { @@ -777,10 +917,6 @@ impl EncodeContext<'a, 'tcx> { }; record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data))); - record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); - record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); - self.encode_stability(def_id); - self.encode_deprecation(def_id); self.encode_item_type(def_id); if variant.ctor_kind == CtorKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); @@ -789,11 +925,9 @@ impl EncodeContext<'a, 'tcx> { self.encode_generics(def_id); self.encode_explicit_predicates(def_id); self.encode_inferred_outlives(def_id); - self.encode_optimized_mir(def_id.expect_local()); - self.encode_promoted_mir(def_id.expect_local()); } - fn encode_info_for_mod(&mut self, id: hir::HirId, md: &hir::Mod<'_>, attrs: &[ast::Attribute]) { + fn encode_info_for_mod(&mut self, id: hir::HirId, md: &hir::Mod<'_>) { let tcx = self.tcx; let local_def_id = tcx.hir().local_def_id(id); let def_id = local_def_id.to_def_id(); @@ -826,9 +960,6 @@ impl EncodeContext<'a, 'tcx> { }; record!(self.tables.kind[def_id] <- EntryKind::Mod(self.lazy(data))); - record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); - record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); - record!(self.tables.attributes[def_id] <- attrs); if self.is_proc_macro { record!(self.tables.children[def_id] <- &[]); } else { @@ -836,8 +967,6 @@ impl EncodeContext<'a, 'tcx> { tcx.hir().local_def_id(item_id.id).local_def_index })); } - self.encode_stability(def_id); - self.encode_deprecation(def_id); } fn encode_field( @@ -846,24 +975,14 @@ impl EncodeContext<'a, 'tcx> { variant_index: VariantIdx, field_index: usize, ) { - let tcx = self.tcx; let variant = &adt_def.variants[variant_index]; let field = &variant.fields[field_index]; let def_id = field.did; debug!("EncodeContext::encode_field({:?})", def_id); - let variant_id = tcx.hir().local_def_id_to_hir_id(variant.def_id.expect_local()); - let variant_data = tcx.hir().expect_variant_data(variant_id); - record!(self.tables.kind[def_id] <- EntryKind::Field); - record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); - record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); - record!(self.tables.attributes[def_id] <- variant_data.fields()[field_index].attrs); - record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); self.encode_ident_span(def_id, field.ident); - self.encode_stability(def_id); - self.encode_deprecation(def_id); self.encode_item_type(def_id); self.encode_generics(def_id); self.encode_explicit_predicates(def_id); @@ -883,11 +1002,6 @@ impl EncodeContext<'a, 'tcx> { }; record!(self.tables.kind[def_id] <- EntryKind::Struct(self.lazy(data), adt_def.repr)); - record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); - record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); - record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); - self.encode_stability(def_id); - self.encode_deprecation(def_id); self.encode_item_type(def_id); if variant.ctor_kind == CtorKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); @@ -896,8 +1010,6 @@ impl EncodeContext<'a, 'tcx> { self.encode_generics(def_id); self.encode_explicit_predicates(def_id); self.encode_inferred_outlives(def_id); - self.encode_optimized_mir(def_id.expect_local()); - self.encode_promoted_mir(def_id.expect_local()); } fn encode_generics(&mut self, def_id: DefId) { @@ -946,29 +1058,25 @@ impl EncodeContext<'a, 'tcx> { hir::Defaultness::Final => span_bug!(ast_item.span, "traits cannot have final items"), }; - record!(self.tables.kind[def_id] <- match trait_item.kind { + match trait_item.kind { ty::AssocKind::Const => { let rendered = rustc_hir_pretty::to_string( &(&self.tcx.hir() as &dyn intravisit::Map<'_>), - |s| s.print_trait_item(ast_item) + |s| s.print_trait_item(ast_item), ); let rendered_const = self.lazy(RenderedConst(rendered)); - EntryKind::AssocConst( + record!(self.tables.kind[def_id] <- EntryKind::AssocConst( container, Default::default(), rendered_const, - ) + )); } ty::AssocKind::Fn => { let fn_data = if let hir::TraitItemKind::Fn(m_sig, m) = &ast_item.kind { let param_names = match *m { - hir::TraitFn::Required(ref names) => { - self.encode_fn_param_names(names) - } - hir::TraitFn::Provided(body) => { - self.encode_fn_param_names_for_body(body) - } + hir::TraitFn::Required(ref names) => self.encode_fn_param_names(names), + hir::TraitFn::Provided(body) => self.encode_fn_param_names_for_body(body), }; FnData { asyncness: m_sig.header.asyncness, @@ -978,24 +1086,18 @@ impl EncodeContext<'a, 'tcx> { } else { bug!() }; - EntryKind::AssocFn(self.lazy(AssocFnData { + record!(self.tables.kind[def_id] <- EntryKind::AssocFn(self.lazy(AssocFnData { fn_data, container, has_self: trait_item.fn_has_self_parameter, - })) + }))); } ty::AssocKind::Type => { self.encode_explicit_item_bounds(def_id); - EntryKind::AssocType(container) + record!(self.tables.kind[def_id] <- EntryKind::AssocType(container)); } - }); - record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); - record!(self.tables.span[def_id] <- ast_item.span); - record!(self.tables.attributes[def_id] <- ast_item.attrs); + } self.encode_ident_span(def_id, ast_item.ident); - self.encode_stability(def_id); - self.encode_const_stability(def_id); - self.encode_deprecation(def_id); match trait_item.kind { ty::AssocKind::Const | ty::AssocKind::Fn => { self.encode_item_type(def_id); @@ -1013,15 +1115,6 @@ impl EncodeContext<'a, 'tcx> { self.encode_generics(def_id); self.encode_explicit_predicates(def_id); self.encode_inferred_outlives(def_id); - - // This should be kept in sync with `PrefetchVisitor.visit_trait_item`. - self.encode_optimized_mir(def_id.expect_local()); - self.encode_promoted_mir(def_id.expect_local()); - } - - fn metadata_output_only(&self) -> bool { - // MIR optimisation can be skipped when we're just interested in the metadata. - !self.tcx.sess.opts.output_types.should_codegen() } fn encode_info_for_impl_item(&mut self, def_id: DefId) { @@ -1040,15 +1133,16 @@ impl EncodeContext<'a, 'tcx> { } }; - record!(self.tables.kind[def_id] <- match impl_item.kind { + match impl_item.kind { ty::AssocKind::Const => { if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind { let qualifs = self.tcx.at(ast_item.span).mir_const_qualif(def_id); - EntryKind::AssocConst( + record!(self.tables.kind[def_id] <- EntryKind::AssocConst( container, qualifs, self.encode_rendered_const_for_body(body_id)) + ); } else { bug!() } @@ -1063,21 +1157,17 @@ impl EncodeContext<'a, 'tcx> { } else { bug!() }; - EntryKind::AssocFn(self.lazy(AssocFnData { + record!(self.tables.kind[def_id] <- EntryKind::AssocFn(self.lazy(AssocFnData { fn_data, container, has_self: impl_item.fn_has_self_parameter, - })) + }))); } - ty::AssocKind::Type => EntryKind::AssocType(container) - }); - record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); - record!(self.tables.span[def_id] <- ast_item.span); - record!(self.tables.attributes[def_id] <- ast_item.attrs); + ty::AssocKind::Type => { + record!(self.tables.kind[def_id] <- EntryKind::AssocType(container)); + } + } self.encode_ident_span(def_id, impl_item.ident); - self.encode_stability(def_id); - self.encode_const_stability(def_id); - self.encode_deprecation(def_id); self.encode_item_type(def_id); if impl_item.kind == ty::AssocKind::Fn { record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); @@ -1086,26 +1176,6 @@ impl EncodeContext<'a, 'tcx> { self.encode_generics(def_id); self.encode_explicit_predicates(def_id); self.encode_inferred_outlives(def_id); - - // The following part should be kept in sync with `PrefetchVisitor.visit_impl_item`. - - let mir = match ast_item.kind { - hir::ImplItemKind::Const(..) => true, - hir::ImplItemKind::Fn(ref sig, _) => { - let generics = self.tcx.generics_of(def_id); - let needs_inline = (generics.requires_monomorphization(self.tcx) - || tcx.codegen_fn_attrs(def_id).requests_inline()) - && !self.metadata_output_only(); - let is_const_fn = sig.header.constness == hir::Constness::Const; - let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir; - needs_inline || is_const_fn || always_encode_mir - } - hir::ImplItemKind::TyAlias(..) => false, - }; - if mir { - self.encode_optimized_mir(def_id.expect_local()); - self.encode_promoted_mir(def_id.expect_local()); - } } fn encode_fn_param_names_for_body(&mut self, body_id: hir::BodyId) -> Lazy<[Ident]> { @@ -1116,27 +1186,47 @@ impl EncodeContext<'a, 'tcx> { self.lazy(param_names.iter()) } - fn encode_optimized_mir(&mut self, def_id: LocalDefId) { - debug!("EntryBuilder::encode_mir({:?})", def_id); - if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id) { - record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id)); + fn encode_mir(&mut self) { + if self.is_proc_macro { + return; + } - let unused = self.tcx.unused_generic_params(def_id); - if !unused.is_empty() { - record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused); + let mut keys_and_jobs = self + .tcx + .mir_keys(LOCAL_CRATE) + .iter() + .filter_map(|&def_id| { + let (encode_const, encode_opt) = should_encode_mir(self.tcx, def_id); + if encode_const || encode_opt { + Some((def_id, encode_const, encode_opt)) + } else { + None + } + }) + .collect::>(); + // Sort everything to ensure a stable order for diagnotics. + keys_and_jobs.sort_by_key(|&(def_id, _, _)| def_id); + for (def_id, encode_const, encode_opt) in keys_and_jobs.into_iter() { + debug_assert!(encode_const || encode_opt); + + debug!("EntryBuilder::encode_mir({:?})", def_id); + if encode_opt { + record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id)); } + if encode_const { + record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- self.tcx.mir_for_ctfe(def_id)); - let abstract_const = self.tcx.mir_abstract_const(def_id); - if let Ok(Some(abstract_const)) = abstract_const { - record!(self.tables.mir_abstract_consts[def_id.to_def_id()] <- abstract_const); + let abstract_const = self.tcx.mir_abstract_const(def_id); + if let Ok(Some(abstract_const)) = abstract_const { + record!(self.tables.mir_abstract_consts[def_id.to_def_id()] <- abstract_const); + } } - } - } - - fn encode_promoted_mir(&mut self, def_id: LocalDefId) { - debug!("EncodeContext::encode_promoted_mir({:?})", def_id); - if self.tcx.mir_keys(LOCAL_CRATE).contains(&def_id) { record!(self.tables.promoted_mir[def_id.to_def_id()] <- self.tcx.promoted_mir(def_id)); + + let unused = self.tcx.unused_generic_params(def_id); + if !unused.is_empty() { + record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused); + } } } @@ -1200,15 +1290,12 @@ impl EncodeContext<'a, 'tcx> { self.encode_ident_span(def_id, item.ident); - record!(self.tables.kind[def_id] <- match item.kind { + let entry_kind = match item.kind { hir::ItemKind::Static(_, hir::Mutability::Mut, _) => EntryKind::MutStatic, hir::ItemKind::Static(_, hir::Mutability::Not, _) => EntryKind::ImmStatic, hir::ItemKind::Const(_, body_id) => { let qualifs = self.tcx.at(item.span).mir_const_qualif(def_id); - EntryKind::Const( - qualifs, - self.encode_rendered_const_for_body(body_id) - ) + EntryKind::Const(qualifs, self.encode_rendered_const_for_body(body_id)) } hir::ItemKind::Fn(ref sig, .., body) => { let data = FnData { @@ -1220,7 +1307,7 @@ impl EncodeContext<'a, 'tcx> { EntryKind::Fn(self.lazy(data)) } hir::ItemKind::Mod(ref m) => { - return self.encode_info_for_mod(item.hir_id, m, &item.attrs); + return self.encode_info_for_mod(item.hir_id, m); } hir::ItemKind::ForeignMod { .. } => EntryKind::ForeignMod, hir::ItemKind::GlobalAsm(..) => EntryKind::GlobalAsm, @@ -1237,61 +1324,61 @@ impl EncodeContext<'a, 'tcx> { // Encode def_ids for each field and method // for methods, write all the stuff get_trait_method // needs to know - let ctor = struct_def.ctor_hir_id().map(|ctor_hir_id| { - self.tcx.hir().local_def_id(ctor_hir_id).local_def_index - }); - - EntryKind::Struct(self.lazy(VariantData { - ctor_kind: variant.ctor_kind, - discr: variant.discr, - ctor, - is_non_exhaustive: variant.is_field_list_non_exhaustive(), - }), adt_def.repr) + let ctor = struct_def + .ctor_hir_id() + .map(|ctor_hir_id| self.tcx.hir().local_def_id(ctor_hir_id).local_def_index); + + EntryKind::Struct( + self.lazy(VariantData { + ctor_kind: variant.ctor_kind, + discr: variant.discr, + ctor, + is_non_exhaustive: variant.is_field_list_non_exhaustive(), + }), + adt_def.repr, + ) } hir::ItemKind::Union(..) => { let adt_def = self.tcx.adt_def(def_id); let variant = adt_def.non_enum_variant(); - EntryKind::Union(self.lazy(VariantData { - ctor_kind: variant.ctor_kind, - discr: variant.discr, - ctor: None, - is_non_exhaustive: variant.is_field_list_non_exhaustive(), - }), adt_def.repr) + EntryKind::Union( + self.lazy(VariantData { + ctor_kind: variant.ctor_kind, + discr: variant.discr, + ctor: None, + is_non_exhaustive: variant.is_field_list_non_exhaustive(), + }), + adt_def.repr, + ) } - hir::ItemKind::Impl { defaultness, .. } => { + hir::ItemKind::Impl(hir::Impl { defaultness, .. }) => { let trait_ref = self.tcx.impl_trait_ref(def_id); let polarity = self.tcx.impl_polarity(def_id); let parent = if let Some(trait_ref) = trait_ref { let trait_def = self.tcx.trait_def(trait_ref.def_id); - trait_def.ancestors(self.tcx, def_id).ok() - .and_then(|mut an| an.nth(1).and_then(|node| { - match node { - specialization_graph::Node::Impl(parent) => Some(parent), - _ => None, - } - })) + trait_def.ancestors(self.tcx, def_id).ok().and_then(|mut an| { + an.nth(1).and_then(|node| match node { + specialization_graph::Node::Impl(parent) => Some(parent), + _ => None, + }) + }) } else { None }; // if this is an impl of `CoerceUnsized`, create its // "unsized info", else just store None - let coerce_unsized_info = - trait_ref.and_then(|t| { - if Some(t.def_id) == self.tcx.lang_items().coerce_unsized_trait() { - Some(self.tcx.at(item.span).coerce_unsized_info(def_id)) - } else { - None - } - }); - - let data = ImplData { - polarity, - defaultness, - parent_impl: parent, - coerce_unsized_info, - }; + let coerce_unsized_info = trait_ref.and_then(|t| { + if Some(t.def_id) == self.tcx.lang_items().coerce_unsized_trait() { + Some(self.tcx.at(item.span).coerce_unsized_info(def_id)) + } else { + None + } + }); + + let data = + ImplData { polarity, defaultness, parent_impl: parent, coerce_unsized_info }; EntryKind::Impl(self.lazy(data)) } @@ -1308,13 +1395,11 @@ impl EncodeContext<'a, 'tcx> { EntryKind::Trait(self.lazy(data)) } hir::ItemKind::TraitAlias(..) => EntryKind::TraitAlias, - hir::ItemKind::ExternCrate(_) | - hir::ItemKind::Use(..) => bug!("cannot encode info for item {:?}", item), - }); - record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); - record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); - record!(self.tables.attributes[def_id] <- item.attrs); - record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); + hir::ItemKind::ExternCrate(_) | hir::ItemKind::Use(..) => { + bug!("cannot encode info for item {:?}", item) + } + }; + record!(self.tables.kind[def_id] <- entry_kind); // FIXME(eddyb) there should be a nicer way to do this. match item.kind { hir::ItemKind::ForeignMod { items, .. } => record!(self.tables.children[def_id] <- @@ -1348,9 +1433,6 @@ impl EncodeContext<'a, 'tcx> { } _ => {} } - self.encode_stability(def_id); - self.encode_const_stability(def_id); - self.encode_deprecation(def_id); match item.kind { hir::ItemKind::Static(..) | hir::ItemKind::Const(..) @@ -1403,42 +1485,17 @@ impl EncodeContext<'a, 'tcx> { } _ => {} } - - // The following part should be kept in sync with `PrefetchVisitor.visit_item`. - - let mir = match item.kind { - hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => true, - hir::ItemKind::Fn(ref sig, ..) => { - let generics = tcx.generics_of(def_id); - let needs_inline = (generics.requires_monomorphization(tcx) - || tcx.codegen_fn_attrs(def_id).requests_inline()) - && !self.metadata_output_only(); - let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir; - needs_inline || sig.header.constness == hir::Constness::Const || always_encode_mir - } - _ => false, - }; - if mir { - self.encode_optimized_mir(def_id.expect_local()); - self.encode_promoted_mir(def_id.expect_local()); - } } /// Serialize the text of exported macros fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef<'_>) { let def_id = self.tcx.hir().local_def_id(macro_def.hir_id).to_def_id(); record!(self.tables.kind[def_id] <- EntryKind::MacroDef(self.lazy(macro_def.ast.clone()))); - record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); - record!(self.tables.span[def_id] <- macro_def.span); - record!(self.tables.attributes[def_id] <- macro_def.attrs); self.encode_ident_span(def_id, macro_def.ident); - self.encode_stability(def_id); - self.encode_deprecation(def_id); } fn encode_info_for_generic_param(&mut self, def_id: DefId, kind: EntryKind, encode_type: bool) { record!(self.tables.kind[def_id] <- kind); - record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); if encode_type { self.encode_item_type(def_id); } @@ -1452,25 +1509,23 @@ impl EncodeContext<'a, 'tcx> { let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); let ty = self.tcx.typeck(def_id).node_type(hir_id); - record!(self.tables.kind[def_id.to_def_id()] <- match ty.kind() { + match ty.kind() { ty::Generator(..) => { let data = self.tcx.generator_kind(def_id).unwrap(); - EntryKind::Generator(data) + record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::Generator(data)); } - ty::Closure(..) => EntryKind::Closure, + ty::Closure(..) => { + record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::Closure); + } _ => bug!("closure that is neither generator nor closure"), - }); - record!(self.tables.span[def_id.to_def_id()] <- self.tcx.def_span(def_id)); - record!(self.tables.attributes[def_id.to_def_id()] <- &self.tcx.get_attrs(def_id.to_def_id())[..]); + } self.encode_item_type(def_id.to_def_id()); if let ty::Closure(def_id, substs) = *ty.kind() { record!(self.tables.fn_sig[def_id] <- substs.as_closure().sig()); } self.encode_generics(def_id.to_def_id()); - self.encode_optimized_mir(def_id); - self.encode_promoted_mir(def_id); } fn encode_info_for_anon_const(&mut self, def_id: LocalDefId) { @@ -1481,13 +1536,10 @@ impl EncodeContext<'a, 'tcx> { let qualifs = self.tcx.mir_const_qualif(def_id); record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::AnonConst(qualifs, const_data)); - record!(self.tables.span[def_id.to_def_id()] <- self.tcx.def_span(def_id)); self.encode_item_type(def_id.to_def_id()); self.encode_generics(def_id.to_def_id()); self.encode_explicit_predicates(def_id.to_def_id()); self.encode_inferred_outlives(def_id.to_def_id()); - self.encode_optimized_mir(def_id); - self.encode_promoted_mir(def_id); } fn encode_native_libraries(&mut self) -> Lazy<[NativeLib]> { @@ -1531,6 +1583,15 @@ impl EncodeContext<'a, 'tcx> { let stability = tcx.lookup_stability(DefId::local(CRATE_DEF_INDEX)).copied(); let macros = self.lazy(hir.krate().proc_macros.iter().map(|p| p.owner.local_def_index)); + record!(self.tables.def_kind[LOCAL_CRATE.as_def_id()] <- DefKind::Mod); + record!(self.tables.span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); + record!(self.tables.attributes[LOCAL_CRATE.as_def_id()] <- tcx.get_attrs(LOCAL_CRATE.as_def_id())); + record!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- tcx.visibility(LOCAL_CRATE.as_def_id())); + if let Some(stability) = stability { + record!(self.tables.stability[LOCAL_CRATE.as_def_id()] <- stability); + } + self.encode_deprecation(LOCAL_CRATE.as_def_id()); + // Normally, this information is encoded when we walk the items // defined in this crate. However, we skip doing that for proc-macro crates, // so we manually encode just the information that we need @@ -1562,6 +1623,7 @@ impl EncodeContext<'a, 'tcx> { def_key.disambiguated_data.data = DefPathData::MacroNs(name); let def_id = DefId::local(id); + record!(self.tables.def_kind[def_id] <- DefKind::Macro(macro_kind)); record!(self.tables.kind[def_id] <- EntryKind::ProcMacro(macro_kind)); record!(self.tables.attributes[def_id] <- attrs); record!(self.tables.def_keys[def_id] <- def_key); @@ -1729,7 +1791,7 @@ impl EncodeContext<'a, 'tcx> { debug!("EncodeContext::encode_info_for_foreign_item({:?})", def_id); - record!(self.tables.kind[def_id] <- match nitem.kind { + match nitem.kind { hir::ForeignItemKind::Fn(_, ref names, _) => { let data = FnData { asyncness: hir::IsAsync::NotAsync, @@ -1740,19 +1802,19 @@ impl EncodeContext<'a, 'tcx> { }, param_names: self.encode_fn_param_names(names), }; - EntryKind::ForeignFn(self.lazy(data)) + record!(self.tables.kind[def_id] <- EntryKind::ForeignFn(self.lazy(data))); } - hir::ForeignItemKind::Static(_, hir::Mutability::Mut) => EntryKind::ForeignMutStatic, - hir::ForeignItemKind::Static(_, hir::Mutability::Not) => EntryKind::ForeignImmStatic, - hir::ForeignItemKind::Type => EntryKind::ForeignType, - }); - record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); - record!(self.tables.span[def_id] <- nitem.span); - record!(self.tables.attributes[def_id] <- nitem.attrs); + hir::ForeignItemKind::Static(_, hir::Mutability::Mut) => { + record!(self.tables.kind[def_id] <- EntryKind::ForeignMutStatic); + } + hir::ForeignItemKind::Static(_, hir::Mutability::Not) => { + record!(self.tables.kind[def_id] <- EntryKind::ForeignImmStatic); + } + hir::ForeignItemKind::Type => { + record!(self.tables.kind[def_id] <- EntryKind::ForeignType); + } + } self.encode_ident_span(def_id, nitem.ident); - self.encode_stability(def_id); - self.encode_const_stability(def_id); - self.encode_deprecation(def_id); self.encode_item_type(def_id); self.encode_inherent_implementations(def_id); if let hir::ForeignItemKind::Fn(..) = nitem.kind { @@ -1818,15 +1880,12 @@ impl EncodeContext<'a, 'tcx> { let def_id = self.tcx.hir().local_def_id(param.hir_id); match param.kind { GenericParamKind::Lifetime { .. } => continue, - GenericParamKind::Type { ref default, .. } => { + GenericParamKind::Type { default, .. } => { self.encode_info_for_generic_param( def_id.to_def_id(), EntryKind::TypeParam, default.is_some(), ); - if default.is_some() { - self.encode_stability(def_id.to_def_id()); - } } GenericParamKind::Const { .. } => { self.encode_info_for_generic_param( @@ -1834,7 +1893,7 @@ impl EncodeContext<'a, 'tcx> { EntryKind::ConstParam, true, ); - // FIXME(const_generics:defaults) + // FIXME(const_generics_defaults) } } } @@ -1945,71 +2004,25 @@ impl<'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'tcx> { /// Used to prefetch queries which will be needed later by metadata encoding. /// Only a subset of the queries are actually prefetched to keep this code smaller. -struct PrefetchVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - mir_keys: &'tcx FxHashSet, -} - -impl<'tcx> PrefetchVisitor<'tcx> { - fn prefetch_mir(&self, def_id: LocalDefId) { - if self.mir_keys.contains(&def_id) { - self.tcx.ensure().optimized_mir(def_id); - self.tcx.ensure().promoted_mir(def_id); - } +fn prefetch_mir(tcx: TyCtxt<'_>) { + if !tcx.sess.opts.output_types.should_codegen() { + // We won't emit MIR, so don't prefetch it. + return; } -} -impl<'tcx, 'v> ParItemLikeVisitor<'v> for PrefetchVisitor<'tcx> { - fn visit_item(&self, item: &hir::Item<'_>) { - // This should be kept in sync with `encode_info_for_item`. - let tcx = self.tcx; - match item.kind { - hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => { - self.prefetch_mir(tcx.hir().local_def_id(item.hir_id)) - } - hir::ItemKind::Fn(ref sig, ..) => { - let def_id = tcx.hir().local_def_id(item.hir_id); - let generics = tcx.generics_of(def_id.to_def_id()); - let needs_inline = generics.requires_monomorphization(tcx) - || tcx.codegen_fn_attrs(def_id.to_def_id()).requests_inline(); - if needs_inline || sig.header.constness == hir::Constness::Const { - self.prefetch_mir(def_id) - } - } - _ => (), - } - } - - fn visit_trait_item(&self, trait_item: &'v hir::TraitItem<'v>) { - // This should be kept in sync with `encode_info_for_trait_item`. - self.prefetch_mir(self.tcx.hir().local_def_id(trait_item.hir_id)); - } + par_iter(tcx.mir_keys(LOCAL_CRATE)).for_each(|&def_id| { + let (encode_const, encode_opt) = should_encode_mir(tcx, def_id); - fn visit_impl_item(&self, impl_item: &'v hir::ImplItem<'v>) { - // This should be kept in sync with `encode_info_for_impl_item`. - let tcx = self.tcx; - match impl_item.kind { - hir::ImplItemKind::Const(..) => { - self.prefetch_mir(tcx.hir().local_def_id(impl_item.hir_id)) - } - hir::ImplItemKind::Fn(ref sig, _) => { - let def_id = tcx.hir().local_def_id(impl_item.hir_id); - let generics = tcx.generics_of(def_id.to_def_id()); - let needs_inline = generics.requires_monomorphization(tcx) - || tcx.codegen_fn_attrs(def_id.to_def_id()).requests_inline(); - let is_const_fn = sig.header.constness == hir::Constness::Const; - if needs_inline || is_const_fn { - self.prefetch_mir(def_id) - } - } - hir::ImplItemKind::TyAlias(..) => (), + if encode_const { + tcx.ensure().mir_for_ctfe(def_id); } - } - - fn visit_foreign_item(&self, _foreign_item: &'v hir::ForeignItem<'v>) { - // This should be kept in sync with `encode_info_for_foreign_item`. - // Foreign items contain no MIR. - } + if encode_opt { + tcx.ensure().optimized_mir(def_id); + } + if encode_opt || encode_const { + tcx.ensure().promoted_mir(def_id); + } + }) } // NOTE(eddyb) The following comment was preserved for posterity, even @@ -2049,19 +2062,7 @@ pub(super) fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata { // Prefetch some queries used by metadata encoding. // This is not necessary for correctness, but is only done for performance reasons. // It can be removed if it turns out to cause trouble or be detrimental to performance. - join( - || { - if !tcx.sess.opts.output_types.should_codegen() { - // We won't emit MIR, so don't prefetch it. - return; - } - tcx.hir().krate().par_visit_all_item_likes(&PrefetchVisitor { - tcx, - mir_keys: tcx.mir_keys(LOCAL_CRATE), - }); - }, - || tcx.exported_symbols(LOCAL_CRATE), - ); + join(|| prefetch_mir(tcx), || tcx.exported_symbols(LOCAL_CRATE)); }, ) .0 diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 5360617890..b44c3bfcac 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -6,7 +6,7 @@ use rustc_attr as attr; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::MetadataRef; use rustc_hir as hir; -use rustc_hir::def::CtorKind; +use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{DefId, DefIndex, DefPathHash}; use rustc_hir::definitions::DefKey; use rustc_hir::lang_items; @@ -279,6 +279,7 @@ macro_rules! define_tables { } define_tables! { + def_kind: Table>, kind: Table>, visibility: Table>, span: Table>, @@ -302,6 +303,7 @@ define_tables! { // As an optimization, a missing entry indicates an empty `&[]`. explicit_item_bounds: Table, Span)])>, mir: Table)>, + mir_for_ctfe: Table)>, promoted_mir: Table>)>, mir_abstract_consts: Table])>, unused_generic_params: Table>>, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 47b7768b41..d33aad3b71 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -26,7 +26,7 @@ rustc_index = { path = "../rustc_index" } rustc_serialize = { path = "../rustc_serialize" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -chalk-ir = "0.36.0" +chalk-ir = "0.55.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } measureme = "9.0.0" rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index d954c8ab5f..1cb7575737 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -1,16 +1,17 @@ -//! This module defines the `DepNode` type which the compiler uses to represent -//! nodes in the dependency graph. +//! Nodes in the dependency graph. //! -//! A `DepNode` consists of a `DepKind` (which -//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc) -//! and a `Fingerprint`, a 128-bit hash value the exact meaning of which +//! A node in the [dependency graph] is represented by a [`DepNode`]. +//! A `DepNode` consists of a [`DepKind`] (which +//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc.) +//! and a [`Fingerprint`], a 128-bit hash value, the exact meaning of which //! depends on the node's `DepKind`. Together, the kind and the fingerprint //! fully identify a dependency node, even across multiple compilation sessions. //! In other words, the value of the fingerprint does not depend on anything //! that is specific to a given compilation session, like an unpredictable -//! interning key (e.g., NodeId, DefId, Symbol) or the numeric value of a +//! interning key (e.g., `NodeId`, `DefId`, `Symbol`) or the numeric value of a //! pointer. The concept behind this could be compared to how git commit hashes -//! uniquely identify a given commit and has a few advantages: +//! uniquely identify a given commit. The fingerprinting approach has +//! a few advantages: //! //! * A `DepNode` can simply be serialized to disk and loaded in another session //! without the need to do any "rebasing" (like we have to do for Spans and @@ -29,9 +30,10 @@ //! contained no `DefId` for thing that had been removed. //! //! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro -//! defines the `DepKind` enum and a corresponding `DepConstructor` enum. The -//! `DepConstructor` enum links a `DepKind` to the parameters that are needed at -//! runtime in order to construct a valid `DepNode` fingerprint. +//! defines the `DepKind` enum. Each `DepKind` has its own parameters that are +//! needed at runtime in order to construct a valid `DepNode` fingerprint. +//! However, only `CompileCodegenUnit` is constructed explicitly (with +//! `make_compile_codegen_unit`). //! //! Because the macro sees what parameters a given `DepKind` requires, it can //! "infer" some properties for each kind of `DepNode`: @@ -44,32 +46,120 @@ //! `DefId` it was computed from. In other cases, too much information gets //! lost during fingerprint computation. //! -//! The `DepConstructor` enum, together with `DepNode::new()`, ensures that only +//! `make_compile_codegen_unit`, together with `DepNode::new()`, ensures that only //! valid `DepNode` instances can be constructed. For example, the API does not //! allow for constructing parameterless `DepNode`s with anything other //! than a zeroed out fingerprint. More generally speaking, it relieves the //! user of the `DepNode` API of having to know how to compute the expected //! fingerprint for a given set of node parameters. +//! +//! [dependency graph]: https://rustc-dev-guide.rust-lang.org/query.html -use crate::mir::interpret::{GlobalId, LitToConstInput}; -use crate::traits; -use crate::traits::query::{ - CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, - CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, - CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, -}; -use crate::ty::subst::{GenericArg, SubstsRef}; -use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use crate::ty::TyCtxt; use rustc_data_structures::fingerprint::Fingerprint; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX}; use rustc_hir::definitions::DefPathHash; use rustc_hir::HirId; use rustc_span::symbol::Symbol; +use rustc_span::DUMMY_SP; use std::hash::Hash; pub use rustc_query_system::dep_graph::{DepContext, DepNodeParams}; +/// This struct stores metadata about each DepKind. +/// +/// Information is retrieved by indexing the `DEP_KINDS` array using the integer value +/// of the `DepKind`. Overall, this allows to implement `DepContext` using this manual +/// jump table instead of large matches. +pub struct DepKindStruct { + /// Whether the DepNode has parameters (query keys). + pub(super) has_params: bool, + + /// Anonymous queries cannot be replayed from one compiler invocation to the next. + /// When their result is needed, it is recomputed. They are useful for fine-grained + /// dependency tracking, and caching within one compiler invocation. + pub(super) is_anon: bool, + + /// Eval-always queries do not track their dependencies, and are always recomputed, even if + /// their inputs have not changed since the last compiler invocation. The result is still + /// cached within one compiler invocation. + pub(super) is_eval_always: bool, + + /// Whether the query key can be recovered from the hashed fingerprint. + /// See [DepNodeParams] trait for the behaviour of each key type. + // FIXME: Make this a simple boolean once DepNodeParams::can_reconstruct_query_key + // can be made a specialized associated const. + can_reconstruct_query_key: fn() -> bool, + + /// The red/green evaluation system will try to mark a specific DepNode in the + /// dependency graph as green by recursively trying to mark the dependencies of + /// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` + /// where we don't know if it is red or green and we therefore actually have + /// to recompute its value in order to find out. Since the only piece of + /// information that we have at that point is the `DepNode` we are trying to + /// re-evaluate, we need some way to re-run a query from just that. This is what + /// `force_from_dep_node()` implements. + /// + /// In the general case, a `DepNode` consists of a `DepKind` and an opaque + /// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint + /// is usually constructed by computing a stable hash of the query-key that the + /// `DepNode` corresponds to. Consequently, it is not in general possible to go + /// back from hash to query-key (since hash functions are not reversible). For + /// this reason `force_from_dep_node()` is expected to fail from time to time + /// because we just cannot find out, from the `DepNode` alone, what the + /// corresponding query-key is and therefore cannot re-run the query. + /// + /// The system deals with this case letting `try_mark_green` fail which forces + /// the root query to be re-evaluated. + /// + /// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. + /// Fortunately, we can use some contextual information that will allow us to + /// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we + /// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a + /// valid `DefPathHash`. Since we also always build a huge table that maps every + /// `DefPathHash` in the current codebase to the corresponding `DefId`, we have + /// everything we need to re-run the query. + /// + /// Take the `mir_promoted` query as an example. Like many other queries, it + /// just has a single parameter: the `DefId` of the item it will compute the + /// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` + /// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` + /// is actually a `DefPathHash`, and can therefore just look up the corresponding + /// `DefId` in `tcx.def_path_hash_to_def_id`. + /// + /// When you implement a new query, it will likely have a corresponding new + /// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As + /// a rule of thumb, if your query takes a `DefId` or `LocalDefId` as sole parameter, + /// then `force_from_dep_node()` should not fail for it. Otherwise, you can just + /// add it to the "We don't have enough information to reconstruct..." group in + /// the match below. + pub(super) force_from_dep_node: fn(tcx: TyCtxt<'_>, dep_node: &DepNode) -> bool, + + /// Invoke a query to put the on-disk cached value in memory. + pub(super) try_load_from_on_disk_cache: fn(TyCtxt<'_>, &DepNode), +} + +impl std::ops::Deref for DepKind { + type Target = DepKindStruct; + fn deref(&self) -> &DepKindStruct { + &DEP_KINDS[*self as usize] + } +} + +impl DepKind { + #[inline(always)] + pub fn can_reconstruct_query_key(&self) -> bool { + // Only fetch the DepKindStruct once. + let data: &DepKindStruct = &**self; + if data.is_anon { + return false; + } + + (data.can_reconstruct_query_key)() + } +} + // erase!() just makes tokens go away. It's used to specify which macro argument // is repeated (i.e., which sub-expression of the macro we are in) but don't need // to actually use any of the arguments. @@ -103,189 +193,140 @@ macro_rules! contains_eval_always_attr { ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_eval_always_attr!($attr) | )* false}); } -macro_rules! define_dep_nodes { - (<$tcx:tt> - $( - [$($attrs:tt)*] - $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* - ,)* - ) => ( - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] - #[allow(non_camel_case_types)] - pub enum DepKind { - $($variant),* - } +#[allow(non_upper_case_globals)] +pub mod dep_kind { + use super::*; + use crate::ty::query::{queries, query_keys}; + use rustc_query_system::query::{force_query, QueryDescription}; - impl DepKind { - #[allow(unreachable_code)] - pub fn can_reconstruct_query_key<$tcx>(&self) -> bool { - match *self { - $( - DepKind :: $variant => { - if contains_anon_attr!($($attrs)*) { - return false; - } - - // tuple args - $({ - return <$tuple_arg_ty as DepNodeParams>> - ::can_reconstruct_query_key(); - })* - - true - } - )* - } - } + // We use this for most things when incr. comp. is turned off. + pub const Null: DepKindStruct = DepKindStruct { + has_params: false, + is_anon: false, + is_eval_always: false, + + can_reconstruct_query_key: || true, + force_from_dep_node: |_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node), + try_load_from_on_disk_cache: |_, _| {}, + }; - pub fn is_anon(&self) -> bool { - match *self { - $( - DepKind :: $variant => { contains_anon_attr!($($attrs)*) } - )* - } - } + pub const TraitSelect: DepKindStruct = DepKindStruct { + has_params: false, + is_anon: true, + is_eval_always: false, - pub fn is_eval_always(&self) -> bool { - match *self { - $( - DepKind :: $variant => { contains_eval_always_attr!($($attrs)*) } - )* - } - } + can_reconstruct_query_key: || true, + force_from_dep_node: |_, _| false, + try_load_from_on_disk_cache: |_, _| {}, + }; - #[allow(unreachable_code)] - pub fn has_params(&self) -> bool { - match *self { - $( - DepKind :: $variant => { - // tuple args - $({ - erase!($tuple_arg_ty); - return true; - })* - - false - } - )* - } - } - } + pub const CompileCodegenUnit: DepKindStruct = DepKindStruct { + has_params: true, + is_anon: false, + is_eval_always: false, - pub struct DepConstructor; + can_reconstruct_query_key: || false, + force_from_dep_node: |_, _| false, + try_load_from_on_disk_cache: |_, _| {}, + }; + + macro_rules! define_query_dep_kinds { + ($( + [$($attrs:tt)*] + $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* + ,)*) => ( + $(pub const $variant: DepKindStruct = { + const has_params: bool = $({ erase!($tuple_arg_ty); true } |)* false; + const is_anon: bool = contains_anon_attr!($($attrs)*); + const is_eval_always: bool = contains_eval_always_attr!($($attrs)*); - #[allow(non_camel_case_types)] - impl DepConstructor { - $( #[inline(always)] - #[allow(unreachable_code, non_snake_case)] - pub fn $variant(_tcx: TyCtxt<'_>, $(arg: $tuple_arg_ty)*) -> DepNode { - // tuple args - $({ - erase!($tuple_arg_ty); - return DepNode::construct(_tcx, DepKind::$variant, &arg) - })* - - return DepNode::construct(_tcx, DepKind::$variant, &()) + fn can_reconstruct_query_key() -> bool { + as DepNodeParams>> + ::can_reconstruct_query_key() } - )* - } - - pub type DepNode = rustc_query_system::dep_graph::DepNode; - - // We keep a lot of `DepNode`s in memory during compilation. It's not - // required that their size stay the same, but we don't want to change - // it inadvertently. This assert just ensures we're aware of any change. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - static_assert_size!(DepNode, 17); - - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - static_assert_size!(DepNode, 24); - - pub trait DepNodeExt: Sized { - /// Construct a DepNode from the given DepKind and DefPathHash. This - /// method will assert that the given DepKind actually requires a - /// single DefId/DefPathHash parameter. - fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> Self; - - /// Extracts the DefId corresponding to this DepNode. This will work - /// if two conditions are met: - /// - /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and - /// 2. the item that the DefPath refers to exists in the current tcx. - /// - /// Condition (1) is determined by the DepKind variant of the - /// DepNode. Condition (2) might not be fulfilled if a DepNode - /// refers to something from the previous compilation session that - /// has been removed. - fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option; - - /// Used in testing - fn from_label_string(label: &str, def_path_hash: DefPathHash) - -> Result; - - /// Used in testing - fn has_label_string(label: &str) -> bool; - } - impl DepNodeExt for DepNode { - /// Construct a DepNode from the given DepKind and DefPathHash. This - /// method will assert that the given DepKind actually requires a - /// single DefId/DefPathHash parameter. - fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> DepNode { - debug_assert!(kind.can_reconstruct_query_key() && kind.has_params()); - DepNode { - kind, - hash: def_path_hash.0.into(), + fn recover<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option> { + as DepNodeParams>>::recover(tcx, dep_node) } - } - /// Extracts the DefId corresponding to this DepNode. This will work - /// if two conditions are met: - /// - /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and - /// 2. the item that the DefPath refers to exists in the current tcx. - /// - /// Condition (1) is determined by the DepKind variant of the - /// DepNode. Condition (2) might not be fulfilled if a DepNode - /// refers to something from the previous compilation session that - /// has been removed. - fn extract_def_id(&self, tcx: TyCtxt<'tcx>) -> Option { - if self.kind.can_reconstruct_query_key() { - tcx.queries.on_disk_cache.as_ref()?.def_path_hash_to_def_id(tcx, DefPathHash(self.hash.into())) - } else { - None + fn force_from_dep_node(tcx: TyCtxt<'_>, dep_node: &DepNode) -> bool { + if is_anon { + return false; + } + + if !can_reconstruct_query_key() { + return false; + } + + if let Some(key) = recover(tcx, dep_node) { + force_query::, _>( + tcx, + key, + DUMMY_SP, + *dep_node + ); + return true; + } + + false } - } - /// Used in testing - fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result { - let kind = match label { - $( - stringify!($variant) => DepKind::$variant, - )* - _ => return Err(()), - }; - - if !kind.can_reconstruct_query_key() { - return Err(()); - } + fn try_load_from_on_disk_cache(tcx: TyCtxt<'_>, dep_node: &DepNode) { + if is_anon { + return + } + + if !can_reconstruct_query_key() { + return + } + + debug_assert!(tcx.dep_graph + .node_color(dep_node) + .map(|c| c.is_green()) + .unwrap_or(false)); - if kind.has_params() { - Ok(DepNode::from_def_path_hash(def_path_hash, kind)) - } else { - Ok(DepNode::new_no_params(kind)) + let key = recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); + if queries::$variant::cache_on_disk(tcx, &key, None) { + let _ = tcx.$variant(key); + } } - } - /// Used in testing - fn has_label_string(label: &str) -> bool { - match label { - $( - stringify!($variant) => true, - )* - _ => false, + DepKindStruct { + has_params, + is_anon, + is_eval_always, + can_reconstruct_query_key, + force_from_dep_node, + try_load_from_on_disk_cache, } + };)* + ); + } + + rustc_dep_node_append!([define_query_dep_kinds!][]); +} + +macro_rules! define_dep_nodes { + (<$tcx:tt> + $( + [$($attrs:tt)*] + $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* + ,)* + ) => ( + static DEP_KINDS: &[DepKindStruct] = &[ $(dep_kind::$variant),* ]; + + /// This enum serves as an index into the `DEP_KINDS` array. + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] + #[allow(non_camel_case_types)] + pub enum DepKind { + $($variant),* + } + + fn dep_kind_from_label_string(label: &str) -> Result { + match label { + $(stringify!($variant) => Ok(DepKind::$variant),)* + _ => Err(()), } } @@ -304,16 +345,122 @@ rustc_dep_node_append!([define_dep_nodes!][ <'tcx> // We use this for most things when incr. comp. is turned off. [] Null, - // Represents metadata from an extern crate. - [eval_always] CrateMetadata(CrateNum), - [anon] TraitSelect, + // WARNING: if `Symbol` is changed, make sure you update `make_compile_codegen_unit` below. [] CompileCodegenUnit(Symbol), ]); +// WARNING: `construct` is generic and does not know that `CompileCodegenUnit` takes `Symbol`s as keys. +// Be very careful changing this type signature! +crate fn make_compile_codegen_unit(tcx: TyCtxt<'_>, name: Symbol) -> DepNode { + DepNode::construct(tcx, DepKind::CompileCodegenUnit, &name) +} + +pub type DepNode = rustc_query_system::dep_graph::DepNode; + +// We keep a lot of `DepNode`s in memory during compilation. It's not +// required that their size stay the same, but we don't want to change +// it inadvertently. This assert just ensures we're aware of any change. +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +static_assert_size!(DepNode, 17); + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +static_assert_size!(DepNode, 24); + +pub trait DepNodeExt: Sized { + /// Construct a DepNode from the given DepKind and DefPathHash. This + /// method will assert that the given DepKind actually requires a + /// single DefId/DefPathHash parameter. + fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> Self; + + /// Extracts the DefId corresponding to this DepNode. This will work + /// if two conditions are met: + /// + /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and + /// 2. the item that the DefPath refers to exists in the current tcx. + /// + /// Condition (1) is determined by the DepKind variant of the + /// DepNode. Condition (2) might not be fulfilled if a DepNode + /// refers to something from the previous compilation session that + /// has been removed. + fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option; + + /// Used in testing + fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result; + + /// Used in testing + fn has_label_string(label: &str) -> bool; +} + +impl DepNodeExt for DepNode { + /// Construct a DepNode from the given DepKind and DefPathHash. This + /// method will assert that the given DepKind actually requires a + /// single DefId/DefPathHash parameter. + fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> DepNode { + debug_assert!(kind.can_reconstruct_query_key() && kind.has_params); + DepNode { kind, hash: def_path_hash.0.into() } + } + + /// Extracts the DefId corresponding to this DepNode. This will work + /// if two conditions are met: + /// + /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and + /// 2. the item that the DefPath refers to exists in the current tcx. + /// + /// Condition (1) is determined by the DepKind variant of the + /// DepNode. Condition (2) might not be fulfilled if a DepNode + /// refers to something from the previous compilation session that + /// has been removed. + fn extract_def_id(&self, tcx: TyCtxt<'tcx>) -> Option { + if self.kind.can_reconstruct_query_key() { + tcx.queries + .on_disk_cache + .as_ref()? + .def_path_hash_to_def_id(tcx, DefPathHash(self.hash.into())) + } else { + None + } + } + + /// Used in testing + fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result { + let kind = dep_kind_from_label_string(label)?; + + if !kind.can_reconstruct_query_key() { + return Err(()); + } + + if kind.has_params { + Ok(DepNode::from_def_path_hash(def_path_hash, kind)) + } else { + Ok(DepNode::new_no_params(kind)) + } + } + + /// Used in testing + fn has_label_string(label: &str) -> bool { + dep_kind_from_label_string(label).is_ok() + } +} + +impl<'tcx> DepNodeParams> for () { + #[inline(always)] + fn can_reconstruct_query_key() -> bool { + true + } + + fn to_fingerprint(&self, _: TyCtxt<'tcx>) -> Fingerprint { + Fingerprint::ZERO + } + + fn recover(_: TyCtxt<'tcx>, _: &DepNode) -> Option { + Some(()) + } +} + impl<'tcx> DepNodeParams> for DefId { - #[inline] + #[inline(always)] fn can_reconstruct_query_key() -> bool { true } @@ -342,7 +489,7 @@ impl<'tcx> DepNodeParams> for DefId { } impl<'tcx> DepNodeParams> for LocalDefId { - #[inline] + #[inline(always)] fn can_reconstruct_query_key() -> bool { true } @@ -361,7 +508,7 @@ impl<'tcx> DepNodeParams> for LocalDefId { } impl<'tcx> DepNodeParams> for CrateNum { - #[inline] + #[inline(always)] fn can_reconstruct_query_key() -> bool { true } @@ -381,7 +528,7 @@ impl<'tcx> DepNodeParams> for CrateNum { } impl<'tcx> DepNodeParams> for (DefId, DefId) { - #[inline] + #[inline(always)] fn can_reconstruct_query_key() -> bool { false } @@ -406,7 +553,7 @@ impl<'tcx> DepNodeParams> for (DefId, DefId) { } impl<'tcx> DepNodeParams> for HirId { - #[inline] + #[inline(always)] fn can_reconstruct_query_key() -> bool { false } diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 728bfef9f4..b88ffa2bb7 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -1,5 +1,4 @@ use crate::ich::StableHashingContext; -use crate::ty::query::try_load_from_on_disk_cache; use crate::ty::{self, TyCtxt}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; @@ -9,13 +8,13 @@ use rustc_hir::def_id::LocalDefId; mod dep_node; -pub(crate) use rustc_query_system::dep_graph::DepNodeParams; pub use rustc_query_system::dep_graph::{ debug, hash_result, DepContext, DepNodeColor, DepNodeIndex, SerializedDepNodeIndex, WorkProduct, WorkProductId, }; -pub use dep_node::{label_strs, DepConstructor, DepKind, DepNode, DepNodeExt}; +crate use dep_node::make_compile_codegen_unit; +pub use dep_node::{label_strs, DepKind, DepNode, DepNodeExt}; pub type DepGraph = rustc_query_system::dep_graph::DepGraph; pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps; @@ -26,18 +25,25 @@ pub type SerializedDepGraph = rustc_query_system::dep_graph::SerializedDepGraph< impl rustc_query_system::dep_graph::DepKind for DepKind { const NULL: Self = DepKind::Null; + #[inline(always)] + fn can_reconstruct_query_key(&self) -> bool { + DepKind::can_reconstruct_query_key(self) + } + + #[inline(always)] fn is_eval_always(&self) -> bool { - DepKind::is_eval_always(self) + self.is_eval_always } + #[inline(always)] fn has_params(&self) -> bool { - DepKind::has_params(self) + self.has_params } fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", node.kind)?; - if !node.kind.has_params() && !node.kind.is_anon() { + if !node.kind.has_params && !node.kind.is_anon { return Ok(()); } @@ -81,10 +87,6 @@ impl rustc_query_system::dep_graph::DepKind for DepKind { op(icx.task_deps) }) } - - fn can_reconstruct_query_key(&self) -> bool { - DepKind::can_reconstruct_query_key(self) - } } impl<'tcx> DepContext for TyCtxt<'tcx> { @@ -114,20 +116,9 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { // be removed. https://github.com/rust-lang/rust/issues/62649 is one such // bug that must be fixed before removing this. match dep_node.kind { - DepKind::hir_owner | DepKind::hir_owner_nodes | DepKind::CrateMetadata => { + DepKind::hir_owner | DepKind::hir_owner_nodes => { if let Some(def_id) = dep_node.extract_def_id(*self) { - if def_id_corresponds_to_hir_dep_node(*self, def_id.expect_local()) { - if dep_node.kind == DepKind::CrateMetadata { - // The `DefPath` has corresponding node, - // and that node should have been marked - // either red or green in `data.colors`. - bug!( - "DepNode {:?} should have been \ - pre-marked as red or green but wasn't.", - dep_node - ); - } - } else { + if !def_id_corresponds_to_hir_dep_node(*self, def_id.expect_local()) { // This `DefPath` does not have a // corresponding `DepNode` (e.g. a // struct field), and the ` DefPath` @@ -153,7 +144,26 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { } debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); - ty::query::force_from_dep_node(*self, dep_node) + + // We must avoid ever having to call `force_from_dep_node()` for a + // `DepNode::codegen_unit`: + // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we + // would always end up having to evaluate the first caller of the + // `codegen_unit` query that *is* reconstructible. This might very well be + // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just + // to re-trigger calling the `codegen_unit` query with the right key. At + // that point we would already have re-done all the work we are trying to + // avoid doing in the first place. + // The solution is simple: Just explicitly call the `codegen_unit` query for + // each CGU, right after partitioning. This way `try_mark_green` will always + // hit the cache instead of having to go through `force_from_dep_node`. + // This assertion makes sure, we actually keep applying the solution above. + debug_assert!( + dep_node.kind != DepKind::codegen_unit, + "calling force_from_dep_node() on DepKind::codegen_unit" + ); + + (dep_node.kind.force_from_dep_node)(*self, dep_node) } fn has_errors_or_delayed_span_bugs(&self) -> bool { @@ -166,7 +176,7 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { // Interactions with on_disk_cache fn try_load_from_on_disk_cache(&self, dep_node: &DepNode) { - try_load_from_on_disk_cache(*self, dep_node) + (dep_node.kind.try_load_from_on_disk_cache)(*self, dep_node) } fn load_diagnostics(&self, prev_dep_node_index: SerializedDepNodeIndex) -> Vec { diff --git a/compiler/rustc_middle/src/hir/map/blocks.rs b/compiler/rustc_middle/src/hir/map/blocks.rs index 6f572a4875..9d392c7b26 100644 --- a/compiler/rustc_middle/src/hir/map/blocks.rs +++ b/compiler/rustc_middle/src/hir/map/blocks.rs @@ -42,37 +42,25 @@ trait MaybeFnLike { impl MaybeFnLike for hir::Item<'_> { fn is_fn_like(&self) -> bool { - match self.kind { - hir::ItemKind::Fn(..) => true, - _ => false, - } + matches!(self.kind, hir::ItemKind::Fn(..)) } } impl MaybeFnLike for hir::ImplItem<'_> { fn is_fn_like(&self) -> bool { - match self.kind { - hir::ImplItemKind::Fn(..) => true, - _ => false, - } + matches!(self.kind, hir::ImplItemKind::Fn(..)) } } impl MaybeFnLike for hir::TraitItem<'_> { fn is_fn_like(&self) -> bool { - match self.kind { - hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => true, - _ => false, - } + matches!(self.kind, hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_))) } } impl MaybeFnLike for hir::Expr<'_> { fn is_fn_like(&self) -> bool { - match self.kind { - hir::ExprKind::Closure(..) => true, - _ => false, - } + matches!(self.kind, hir::ExprKind::Closure(..)) } } diff --git a/compiler/rustc_middle/src/hir/map/collector.rs b/compiler/rustc_middle/src/hir/map/collector.rs index 82cfca4f17..872fcb0f58 100644 --- a/compiler/rustc_middle/src/hir/map/collector.rs +++ b/compiler/rustc_middle/src/hir/map/collector.rs @@ -529,13 +529,22 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { } fn visit_macro_def(&mut self, macro_def: &'hir MacroDef<'hir>) { - self.with_dep_node_owner(macro_def.hir_id.owner, macro_def, |this, hash| { - this.insert_with_hash( - macro_def.span, - macro_def.hir_id, - Node::MacroDef(macro_def), - hash, - ); + // Exported macros are visited directly from the crate root, + // so they do not have `parent_node` set. + // Find the correct enclosing module from their DefKey. + let def_key = self.definitions.def_key(macro_def.hir_id.owner); + let parent = def_key.parent.map_or(hir::CRATE_HIR_ID, |local_def_index| { + self.definitions.local_def_id_to_hir_id(LocalDefId { local_def_index }) + }); + self.with_parent(parent, |this| { + this.with_dep_node_owner(macro_def.hir_id.owner, macro_def, |this, hash| { + this.insert_with_hash( + macro_def.span, + macro_def.hir_id, + Node::MacroDef(macro_def), + hash, + ); + }) }); } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 598e28c1a3..ee12c0e786 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1,7 +1,6 @@ use self::collector::NodeCollector; use crate::hir::{Owner, OwnerNodes}; -use crate::ty::query::Providers; use crate::ty::TyCtxt; use rustc_ast as ast; use rustc_data_structures::svh::Svh; @@ -9,6 +8,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, Definitions}; use rustc_hir::intravisit; +use rustc_hir::intravisit::Visitor; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::*; use rustc_index::vec::IndexVec; @@ -31,7 +31,7 @@ pub struct Entry<'hir> { impl<'hir> Entry<'hir> { fn parent_node(self) -> Option { match self.node { - Node::Crate(_) | Node::MacroDef(_) => None, + Node::Crate(_) => None, _ => Some(self.parent), } } @@ -86,11 +86,13 @@ fn is_body_owner<'hir>(node: Node<'hir>, hir_id: HirId) -> bool { } } +#[derive(Debug)] pub(super) struct HirOwnerData<'hir> { pub(super) signature: Option<&'hir Owner<'hir>>, pub(super) with_bodies: Option<&'hir mut OwnerNodes<'hir>>, } +#[derive(Debug)] pub struct IndexedHir<'hir> { /// The SVH of the local crate. pub crate_hash: Svh, @@ -183,14 +185,18 @@ impl<'hir> Map<'hir> { self.tcx.definitions.opt_local_def_id_to_hir_id(def_id) } - pub fn def_kind(&self, local_def_id: LocalDefId) -> DefKind { + pub fn iter_local_def_id(&self) -> impl Iterator + '_ { + self.tcx.definitions.iter_local_def_id() + } + + pub fn opt_def_kind(&self, local_def_id: LocalDefId) -> Option { // FIXME(eddyb) support `find` on the crate root. if local_def_id.to_def_id().index == CRATE_DEF_INDEX { - return DefKind::Mod; + return Some(DefKind::Mod); } let hir_id = self.local_def_id_to_hir_id(local_def_id); - match self.get(hir_id) { + let def_kind = match self.find(hir_id)? { Node::Item(item) => match item.kind { ItemKind::Static(..) => DefKind::Static, ItemKind::Const(..) => DefKind::Const, @@ -249,6 +255,7 @@ impl<'hir> Map<'hir> { GenericParamKind::Type { .. } => DefKind::TyParam, GenericParamKind::Const { .. } => DefKind::ConstParam, }, + Node::Crate(_) => DefKind::Mod, Node::Stmt(_) | Node::PathSegment(_) | Node::Ty(_) @@ -260,9 +267,14 @@ impl<'hir> Map<'hir> { | Node::Arm(_) | Node::Lifetime(_) | Node::Visibility(_) - | Node::Block(_) - | Node::Crate(_) => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), - } + | Node::Block(_) => return None, + }; + Some(def_kind) + } + + pub fn def_kind(&self, local_def_id: LocalDefId) -> DefKind { + self.opt_def_kind(local_def_id) + .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", local_def_id)) } fn find_entry(&self, id: HirId) -> Option> { @@ -379,7 +391,7 @@ impl<'hir> Map<'hir> { pub fn body_param_names(&self, id: BodyId) -> impl Iterator + 'hir { self.body(id).params.iter().map(|arg| match arg.pat.kind { PatKind::Binding(_, _, ident, _) => ident, - _ => Ident::new(kw::Invalid, rustc_span::DUMMY_SP), + _ => Ident::new(kw::Empty, rustc_span::DUMMY_SP), }) } @@ -483,6 +495,15 @@ impl<'hir> Map<'hir> { } } + pub fn visit_exported_macros_in_krate(&self, visitor: &mut V) + where + V: Visitor<'hir>, + { + for id in self.krate().exported_macros { + visitor.visit_macro_def(self.expect_macro_def(id.hir_id)); + } + } + /// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found. pub fn get(&self, id: HirId) -> Node<'hir> { self.find(id).unwrap_or_else(|| bug!("couldn't find hir id {} in the HIR map", id)) @@ -505,7 +526,7 @@ impl<'hir> Map<'hir> { | ItemKind::Union(_, generics) | ItemKind::Trait(_, _, generics, ..) | ItemKind::TraitAlias(generics, _) - | ItemKind::Impl { generics, .. }, + | ItemKind::Impl(Impl { generics, .. }), .. }) => Some(generics), _ => None, @@ -514,9 +535,7 @@ impl<'hir> Map<'hir> { /// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found. pub fn find(&self, hir_id: HirId) -> Option> { - self.find_entry(hir_id).and_then(|entry| { - if let Node::Crate(..) = entry.node { None } else { Some(entry.node) } - }) + self.find_entry(hir_id).map(|entry| entry.node) } /// Similar to `get_parent`; returns the parent HIR Id, or just `hir_id` if there @@ -550,13 +569,24 @@ impl<'hir> Map<'hir> { self.find(self.get_parent_node(id)), Some( Node::Item(_) - | Node::TraitItem(_) - | Node::ImplItem(_) - | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }), + | Node::TraitItem(_) + | Node::ImplItem(_) + | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }), ) ) } + /// Checks if the node is left-hand side of an assignment. + pub fn is_lhs(&self, id: HirId) -> bool { + match self.find(self.get_parent_node(id)) { + Some(Node::Expr(expr)) => match expr.kind { + ExprKind::Assign(lhs, _rhs, _span) => lhs.hir_id == id, + _ => false, + }, + _ => false, + } + } + /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context. /// Used exclusively for diagnostics, to avoid suggestion function calls. pub fn is_inside_const_context(&self, hir_id: HirId) -> bool { @@ -658,12 +688,12 @@ impl<'hir> Map<'hir> { CRATE_HIR_ID } - /// When on a match arm tail expression or on a match arm, give back the enclosing `match` - /// expression. + /// When on an if expression, a match arm tail expression or a match arm, give back + /// the enclosing `if` or `match` expression. /// - /// Used by error reporting when there's a type error in a match arm caused by the `match` + /// Used by error reporting when there's a type error in an if or match arm caused by the /// expression needing to be unit. - pub fn get_match_if_cause(&self, hir_id: HirId) -> Option<&'hir Expr<'hir>> { + pub fn get_if_cause(&self, hir_id: HirId) -> Option<&'hir Expr<'hir>> { for (_, node) in self.parent_iter(hir_id) { match node { Node::Item(_) @@ -671,7 +701,9 @@ impl<'hir> Map<'hir> { | Node::TraitItem(_) | Node::ImplItem(_) | Node::Stmt(Stmt { kind: StmtKind::Local(_), .. }) => break, - Node::Expr(expr @ Expr { kind: ExprKind::Match(..), .. }) => return Some(expr), + Node::Expr(expr @ Expr { kind: ExprKind::If(..) | ExprKind::Match(..), .. }) => { + return Some(expr); + } _ => {} } } @@ -710,15 +742,10 @@ impl<'hir> Map<'hir> { let mut scope = id; loop { scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID); - if scope == CRATE_HIR_ID { - return CRATE_HIR_ID; - } - match self.get(scope) { - Node::Block(_) => {} - _ => break, + if scope == CRATE_HIR_ID || !matches!(self.get(scope), Node::Block(_)) { + return scope; } } - scope } pub fn get_parent_did(&self, id: HirId) -> LocalDefId { @@ -785,6 +812,13 @@ impl<'hir> Map<'hir> { } } + pub fn expect_macro_def(&self, id: HirId) -> &'hir MacroDef<'hir> { + match self.find(id) { + Some(Node::MacroDef(macro_def)) => macro_def, + _ => bug!("expected macro def, found {}", self.node_to_string(id)), + } + } + pub fn expect_expr(&self, id: HirId) -> &'hir Expr<'hir> { match self.find(id) { Some(Node::Expr(expr)) => expr, @@ -804,6 +838,7 @@ impl<'hir> Map<'hir> { Node::GenericParam(param) => param.name.ident().name, Node::Binding(&Pat { kind: PatKind::Binding(_, _, l, _), .. }) => l.name, Node::Ctor(..) => self.name(self.get_parent_item(id)), + Node::MacroDef(md) => md.ident.name, _ => return None, }) } @@ -818,7 +853,7 @@ impl<'hir> Map<'hir> { /// Given a node ID, gets a list of attributes associated with the AST /// corresponding to the node-ID. pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] { - let attrs = self.find_entry(id).map(|entry| match entry.node { + self.find_entry(id).map_or(&[], |entry| match entry.node { Node::Param(a) => &a.attrs[..], Node::Local(l) => &l.attrs[..], Node::Item(i) => &i.attrs[..], @@ -845,57 +880,61 @@ impl<'hir> Map<'hir> { | Node::Block(..) | Node::Lifetime(..) | Node::Visibility(..) => &[], - }); - attrs.unwrap_or(&[]) + }) } /// Gets the span of the definition of the specified HIR node. /// This is used by `tcx.get_span` pub fn span(&self, hir_id: HirId) -> Span { - match self.find_entry(hir_id).map(|entry| entry.node) { - Some(Node::Param(param)) => param.span, - Some(Node::Item(item)) => match &item.kind { + self.opt_span(hir_id) + .unwrap_or_else(|| bug!("hir::map::Map::span: id not in map: {:?}", hir_id)) + } + + pub fn opt_span(&self, hir_id: HirId) -> Option { + let span = match self.find_entry(hir_id)?.node { + Node::Param(param) => param.span, + Node::Item(item) => match &item.kind { ItemKind::Fn(sig, _, _) => sig.span, _ => item.span, }, - Some(Node::ForeignItem(foreign_item)) => foreign_item.span, - Some(Node::TraitItem(trait_item)) => match &trait_item.kind { + Node::ForeignItem(foreign_item) => foreign_item.span, + Node::TraitItem(trait_item) => match &trait_item.kind { TraitItemKind::Fn(sig, _) => sig.span, _ => trait_item.span, }, - Some(Node::ImplItem(impl_item)) => match &impl_item.kind { + Node::ImplItem(impl_item) => match &impl_item.kind { ImplItemKind::Fn(sig, _) => sig.span, _ => impl_item.span, }, - Some(Node::Variant(variant)) => variant.span, - Some(Node::Field(field)) => field.span, - Some(Node::AnonConst(constant)) => self.body(constant.body).value.span, - Some(Node::Expr(expr)) => expr.span, - Some(Node::Stmt(stmt)) => stmt.span, - Some(Node::PathSegment(seg)) => seg.ident.span, - Some(Node::Ty(ty)) => ty.span, - Some(Node::TraitRef(tr)) => tr.path.span, - Some(Node::Binding(pat)) => pat.span, - Some(Node::Pat(pat)) => pat.span, - Some(Node::Arm(arm)) => arm.span, - Some(Node::Block(block)) => block.span, - Some(Node::Ctor(..)) => match self.find(self.get_parent_node(hir_id)) { - Some(Node::Item(item)) => item.span, - Some(Node::Variant(variant)) => variant.span, + Node::Variant(variant) => variant.span, + Node::Field(field) => field.span, + Node::AnonConst(constant) => self.body(constant.body).value.span, + Node::Expr(expr) => expr.span, + Node::Stmt(stmt) => stmt.span, + Node::PathSegment(seg) => seg.ident.span, + Node::Ty(ty) => ty.span, + Node::TraitRef(tr) => tr.path.span, + Node::Binding(pat) => pat.span, + Node::Pat(pat) => pat.span, + Node::Arm(arm) => arm.span, + Node::Block(block) => block.span, + Node::Ctor(..) => match self.find(self.get_parent_node(hir_id))? { + Node::Item(item) => item.span, + Node::Variant(variant) => variant.span, _ => unreachable!(), }, - Some(Node::Lifetime(lifetime)) => lifetime.span, - Some(Node::GenericParam(param)) => param.span, - Some(Node::Visibility(&Spanned { + Node::Lifetime(lifetime) => lifetime.span, + Node::GenericParam(param) => param.span, + Node::Visibility(&Spanned { node: VisibilityKind::Restricted { ref path, .. }, .. - })) => path.span, - Some(Node::Visibility(v)) => bug!("unexpected Visibility {:?}", v), - Some(Node::Local(local)) => local.span, - Some(Node::MacroDef(macro_def)) => macro_def.span, - Some(Node::Crate(item)) => item.span, - None => bug!("hir::map::Map::span: id not in map: {:?}", hir_id), - } + }) => path.span, + Node::Visibility(v) => bug!("unexpected Visibility {:?}", v), + Node::Local(local) => local.span, + Node::MacroDef(macro_def) => macro_def.span, + Node::Crate(item) => item.span, + }; + Some(span) } /// Like `hir.span()`, but includes the body of function items @@ -911,7 +950,7 @@ impl<'hir> Map<'hir> { } pub fn span_if_local(&self, id: DefId) -> Option { - id.as_local().map(|id| self.span(self.local_def_id_to_hir_id(id))) + id.as_local().and_then(|id| self.opt_span(self.local_def_id_to_hir_id(id))) } pub fn res_span(&self, res: Res) -> Option { @@ -1105,7 +1144,3 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String { None => format!("unknown node{}", id_str), } } - -pub fn provide(providers: &mut Providers) { - providers.def_kind = |tcx, def_id| tcx.hir().def_kind(def_id.expect_local()); -} diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index ae3b30217c..6934e06d4c 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -15,7 +15,9 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE}; use rustc_hir::*; use rustc_index::vec::IndexVec; +use rustc_span::DUMMY_SP; +#[derive(Debug)] pub struct Owner<'tcx> { parent: HirId, node: Node<'tcx>, @@ -31,12 +33,13 @@ impl<'a, 'tcx> HashStable> for Owner<'tcx> { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ParentedNode<'tcx> { parent: ItemLocalId, node: Node<'tcx>, } +#[derive(Debug)] pub struct OwnerNodes<'tcx> { hash: Fingerprint, nodes: IndexVec>>, @@ -77,6 +80,7 @@ pub fn provide(providers: &mut Providers) { }; providers.hir_owner = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].signature; providers.hir_owner_nodes = |tcx, id| tcx.index_hir(LOCAL_CRATE).map[id].with_bodies.as_deref(); + providers.def_span = |tcx, def_id| tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP); providers.fn_arg_names = |tcx, id| { let hir = tcx.hir(); let hir_id = hir.local_def_id_to_hir_id(id.expect_local()); @@ -92,5 +96,5 @@ pub fn provide(providers: &mut Providers) { span_bug!(hir.span(hir_id), "fn_arg_names: unexpected item {:?}", id); } }; - map::provide(providers); + providers.opt_def_kind = |tcx, def_id| tcx.hir().opt_def_kind(def_id.expect_local()); } diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index 1e2e9df88c..00db19019c 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -110,10 +110,7 @@ impl<'tcx> PlaceWithHirId<'tcx> { base: PlaceBase, projections: Vec>, ) -> PlaceWithHirId<'tcx> { - PlaceWithHirId { - hir_id: hir_id, - place: Place { base_ty: base_ty, base: base, projections: projections }, - } + PlaceWithHirId { hir_id, place: Place { base_ty, base, projections } } } } diff --git a/compiler/rustc_middle/src/ich/hcx.rs b/compiler/rustc_middle/src/ich/hcx.rs index 084fe4cfa1..51b650e5ad 100644 --- a/compiler/rustc_middle/src/ich/hcx.rs +++ b/compiler/rustc_middle/src/ich/hcx.rs @@ -12,11 +12,12 @@ use rustc_hir::definitions::{DefPathHash, Definitions}; use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, CachingSourceMapView, SourceFile}; +use rustc_span::{BytePos, CachingSourceMapView, SourceFile, SpanData}; use rustc_span::def_id::{CrateNum, CRATE_DEF_INDEX}; use smallvec::SmallVec; use std::cmp::Ord; +use std::thread::LocalKey; fn compute_ignored_attr_names() -> FxHashSet { debug_assert!(!ich::IGNORED_ATTRIBUTES.is_empty()); @@ -242,12 +243,26 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { hcx.def_path_hash(def_id).hash_stable(hcx, hasher); } + fn expn_id_cache() -> &'static LocalKey { + thread_local! { + static CACHE: rustc_span::ExpnIdCache = Default::default(); + } + &CACHE + } + fn byte_pos_to_line_and_col( &mut self, byte: BytePos, ) -> Option<(Lrc, usize, BytePos)> { self.source_map().byte_pos_to_line_and_col(byte) } + + fn span_data_to_lines_and_cols( + &mut self, + span: &SpanData, + ) -> Option<(Lrc, usize, BytePos, usize, BytePos)> { + self.source_map().span_data_to_lines_and_cols(span) + } } pub fn hash_stable_trait_impls<'a>( diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index 16e9aafb25..8318bdefc8 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -1,4 +1,4 @@ -use crate::ty::{self, FloatVarValue, InferConst, IntVarValue, Ty, TyCtxt}; +use crate::ty::{self, InferConst, Ty, TyCtxt}; use rustc_data_structures::snapshot_vec; use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify::{ @@ -15,36 +15,6 @@ pub trait ToType { fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; } -/// Raw `TyVid` are used as the unification key for `sub_relations`; -/// they carry no values. -impl UnifyKey for ty::TyVid { - type Value = (); - fn index(&self) -> u32 { - self.index - } - fn from_index(i: u32) -> ty::TyVid { - ty::TyVid { index: i } - } - fn tag() -> &'static str { - "TyVid" - } -} - -impl UnifyKey for ty::IntVid { - type Value = Option; - fn index(&self) -> u32 { - self.index - } - fn from_index(i: u32) -> ty::IntVid { - ty::IntVid { index: i } - } - fn tag() -> &'static str { - "IntVid" - } -} - -impl EqUnifyValue for IntVarValue {} - #[derive(PartialEq, Copy, Clone, Debug)] pub struct RegionVidKey { /// The minimum region vid in the unification set. This is needed @@ -80,7 +50,7 @@ impl UnifyKey for ty::RegionVid { } } -impl ToType for IntVarValue { +impl ToType for ty::IntVarValue { fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { match *self { ty::IntType(i) => tcx.mk_mach_int(i), @@ -89,24 +59,7 @@ impl ToType for IntVarValue { } } -// Floating point type keys - -impl UnifyKey for ty::FloatVid { - type Value = Option; - fn index(&self) -> u32 { - self.index - } - fn from_index(i: u32) -> ty::FloatVid { - ty::FloatVid { index: i } - } - fn tag() -> &'static str { - "FloatVid" - } -} - -impl EqUnifyValue for FloatVarValue {} - -impl ToType for FloatVarValue { +impl ToType for ty::FloatVarValue { fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { tcx.mk_mach_float(self.0) } diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index bd724a5830..ca73481b21 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -49,22 +49,24 @@ impl LintLevelSource { } /// A tuple of a lint level and its source. -pub type LevelSource = (Level, LintLevelSource); +pub type LevelAndSource = (Level, LintLevelSource); +#[derive(Debug)] pub struct LintLevelSets { pub list: Vec, pub lint_cap: Level, } +#[derive(Debug)] pub enum LintSet { CommandLine { // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which // flag. - specs: FxHashMap, + specs: FxHashMap, }, Node { - specs: FxHashMap, + specs: FxHashMap, parent: u32, }, } @@ -78,9 +80,9 @@ impl LintLevelSets { &self, lint: &'static Lint, idx: u32, - aux: Option<&FxHashMap>, + aux: Option<&FxHashMap>, sess: &Session, - ) -> LevelSource { + ) -> LevelAndSource { let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux); // If `level` is none then we actually assume the default level for this @@ -121,7 +123,7 @@ impl LintLevelSets { &self, id: LintId, mut idx: u32, - aux: Option<&FxHashMap>, + aux: Option<&FxHashMap>, ) -> (Option, LintLevelSource) { if let Some(specs) = aux { if let Some(&(level, src)) = specs.get(&id) { @@ -147,6 +149,7 @@ impl LintLevelSets { } } +#[derive(Debug)] pub struct LintLevelMap { pub sets: LintLevelSets, pub id_to_set: FxHashMap, @@ -165,7 +168,7 @@ impl LintLevelMap { lint: &'static Lint, id: HirId, session: &Session, - ) -> Option { + ) -> Option { self.id_to_set.get(&id).map(|idx| self.sets.get_lint_level(lint, *idx, None, session)) } } diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index a4363bb580..5f2ffda642 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -3,7 +3,7 @@ use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_session::config::SanitizerSet; use rustc_span::symbol::Symbol; -#[derive(Clone, TyEncodable, TyDecodable, HashStable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)] pub struct CodegenFnAttrs { pub flags: CodegenFnAttrFlags, /// Parsed representation of the `#[inline]` attribute diff --git a/compiler/rustc_middle/src/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs index 6d2c43874b..4f1ca968c3 100644 --- a/compiler/rustc_middle/src/middle/cstore.rs +++ b/compiler/rustc_middle/src/middle/cstore.rs @@ -96,7 +96,7 @@ pub struct NativeLib { pub wasm_import_module: Option, } -#[derive(Clone, TyEncodable, TyDecodable, HashStable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)] pub struct ForeignModule { pub foreign_items: Vec, pub def_id: DefId, diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index 9bc9ca6707..a369e85306 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -7,7 +7,7 @@ pub mod lib_features { use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::symbol::Symbol; - #[derive(HashStable)] + #[derive(HashStable, Debug)] pub struct LibFeatures { // A map from feature to stabilisation version. pub stable: FxHashMap, diff --git a/compiler/rustc_middle/src/middle/resolve_lifetime.rs b/compiler/rustc_middle/src/middle/resolve_lifetime.rs index 3d0144e9c8..1b7d0e620a 100644 --- a/compiler/rustc_middle/src/middle/resolve_lifetime.rs +++ b/compiler/rustc_middle/src/middle/resolve_lifetime.rs @@ -68,7 +68,7 @@ pub type ObjectLifetimeDefault = Set1; /// Maps the id of each lifetime reference to the lifetime decl /// that it corresponds to. -#[derive(Default, HashStable)] +#[derive(Default, HashStable, Debug)] pub struct ResolveLifetimes { /// Maps from every use of a named (not anonymous) lifetime to a /// `Region` describing how that region is bound diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 4f08057a7e..89ca8eed39 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -36,7 +36,7 @@ impl StabilityLevel { } /// An entry in the `depr_map`. -#[derive(Clone, HashStable)] +#[derive(Clone, HashStable, Debug)] pub struct DeprecationEntry { /// The metadata of the attribute associated with this entry. pub attr: Deprecation, @@ -63,7 +63,7 @@ impl DeprecationEntry { } /// A stability index, giving the stability level for items and methods. -#[derive(HashStable)] +#[derive(HashStable, Debug)] pub struct Index<'tcx> { /// This is mostly a cache, except the stabilities of local items /// are filled by the annotator. diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 8a6bf9dff7..95096d0fb7 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -118,17 +118,11 @@ impl CoverageKind { } pub fn is_counter(&self) -> bool { - match self { - Self::Counter { .. } => true, - _ => false, - } + matches!(self, Self::Counter { .. }) } pub fn is_expression(&self) -> bool { - match self { - Self::Expression { .. } => true, - _ => false, - } + matches!(self, Self::Expression { .. }) } pub fn is_unreachable(&self) -> bool { diff --git a/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs new file mode 100644 index 0000000000..5f028975bd --- /dev/null +++ b/compiler/rustc_middle/src/mir/graph_cyclic_cache.rs @@ -0,0 +1,62 @@ +use rustc_data_structures::graph::{ + self, DirectedGraph, WithNumNodes, WithStartNode, WithSuccessors, +}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::OnceCell; +use rustc_serialize as serialize; + +/// Helper type to cache the result of `graph::is_cyclic`. +#[derive(Clone, Debug)] +pub(super) struct GraphIsCyclicCache { + cache: OnceCell, +} + +impl GraphIsCyclicCache { + #[inline] + pub(super) fn new() -> Self { + GraphIsCyclicCache { cache: OnceCell::new() } + } + + pub(super) fn is_cyclic(&self, graph: &G) -> bool + where + G: ?Sized + DirectedGraph + WithStartNode + WithSuccessors + WithNumNodes, + { + *self.cache.get_or_init(|| graph::is_cyclic(graph)) + } + + /// Invalidates the cache. + #[inline] + pub(super) fn invalidate(&mut self) { + // Invalidating the cache requires mutating the MIR, which in turn requires a unique + // reference (`&mut`) to the `mir::Body`. Because of this, we can assume that all + // callers of `invalidate` have a unique reference to the MIR and thus to the + // cache. This means we never need to do synchronization when `invalidate` is called, + // we can simply reinitialize the `OnceCell`. + self.cache = OnceCell::new(); + } +} + +impl serialize::Encodable for GraphIsCyclicCache { + #[inline] + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + serialize::Encodable::encode(&(), s) + } +} + +impl serialize::Decodable for GraphIsCyclicCache { + #[inline] + fn decode(d: &mut D) -> Result { + serialize::Decodable::decode(d).map(|_v: ()| Self::new()) + } +} + +impl HashStable for GraphIsCyclicCache { + #[inline] + fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) { + // do nothing + } +} + +TrivialTypeFoldableAndLiftImpls! { + GraphIsCyclicCache, +} diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 397d2ffd56..cf931ece71 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -127,6 +127,8 @@ pub enum InvalidProgramInfo<'tcx> { Layout(layout::LayoutError<'tcx>), /// An invalid transmute happened. TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>), + /// SizeOf of unsized type was requested. + SizeOfUnsizedType(Ty<'tcx>), } impl fmt::Display for InvalidProgramInfo<'_> { @@ -144,6 +146,7 @@ impl fmt::Display for InvalidProgramInfo<'_> { "transmuting `{}` to `{}` is not possible, because these types do not have the same size", from_ty, to_ty ), + SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{}`", ty), } } } diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 80b5864213..55fe5f971e 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -588,12 +588,3 @@ pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result u64 { - // The only tricky part here is if value == i64::MIN. In that case, - // wrapping_abs() returns i64::MIN == -2^63. Casting this value to a u64 - // gives 2^63, the correct value. - value.wrapping_abs() as u64 -} diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index e3d5a08561..8774b48fb3 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -1,4 +1,4 @@ -use super::{uabs, AllocId, InterpResult}; +use super::{AllocId, InterpResult}; use rustc_macros::HashStable; use rustc_target::abi::{HasDataLayout, Size}; @@ -57,7 +57,7 @@ pub trait PointerArithmetic: HasDataLayout { #[inline] fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) { // We need to make sure that i fits in a machine isize. - let n = uabs(i); + let n = i.unsigned_abs(); if i >= 0 { let (val, over) = self.overflowing_offset(val, n); (val, over || i > self.machine_isize_max()) diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 5e97862ecf..4bb39fe4a5 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -13,7 +13,7 @@ use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt}; use super::{AllocId, Allocation, InterpResult, Pointer, PointerArithmetic}; /// Represents the result of const evaluation via the `eval_to_allocation` query. -#[derive(Clone, HashStable, TyEncodable, TyDecodable)] +#[derive(Clone, HashStable, TyEncodable, TyDecodable, Debug)] pub struct ConstAlloc<'tcx> { // the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory` // (so you can use `AllocMap::unwrap_memory`). @@ -96,7 +96,7 @@ impl<'tcx> ConstValue<'tcx> { } /// A `Scalar` represents an immediate, primitive value existing outside of a -/// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in +/// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 16 bytes in /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes /// of a simple value or a pointer into another `Allocation` #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)] diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index ad48c35104..718e81c84e 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -35,11 +35,13 @@ use std::ops::{ControlFlow, Index, IndexMut}; use std::slice; use std::{iter, mem, option}; +use self::graph_cyclic_cache::GraphIsCyclicCache; use self::predecessors::{PredecessorCache, Predecessors}; pub use self::query::*; pub mod abstract_const; pub mod coverage; +mod graph_cyclic_cache; pub mod interpret; pub mod mono; mod predecessors; @@ -52,7 +54,7 @@ mod type_foldable; pub mod visit; /// Types for locals -type LocalDecls<'tcx> = IndexVec>; +pub type LocalDecls<'tcx> = IndexVec>; pub trait HasLocalDecls<'tcx> { fn local_decls(&self) -> &LocalDecls<'tcx>; @@ -227,6 +229,7 @@ pub struct Body<'tcx> { pub is_polymorphic: bool, predecessor_cache: PredecessorCache, + is_cyclic: GraphIsCyclicCache, } impl<'tcx> Body<'tcx> { @@ -267,6 +270,7 @@ impl<'tcx> Body<'tcx> { required_consts: Vec::new(), is_polymorphic: false, predecessor_cache: PredecessorCache::new(), + is_cyclic: GraphIsCyclicCache::new(), }; body.is_polymorphic = body.has_param_types_or_consts(); body @@ -296,6 +300,7 @@ impl<'tcx> Body<'tcx> { var_debug_info: Vec::new(), is_polymorphic: false, predecessor_cache: PredecessorCache::new(), + is_cyclic: GraphIsCyclicCache::new(), }; body.is_polymorphic = body.has_param_types_or_consts(); body @@ -309,11 +314,12 @@ impl<'tcx> Body<'tcx> { #[inline] pub fn basic_blocks_mut(&mut self) -> &mut IndexVec> { // Because the user could mutate basic block terminators via this reference, we need to - // invalidate the predecessor cache. + // invalidate the caches. // // FIXME: Use a finer-grained API for this, so only transformations that alter terminators - // invalidate the predecessor cache. + // invalidate the caches. self.predecessor_cache.invalidate(); + self.is_cyclic.invalidate(); &mut self.basic_blocks } @@ -322,6 +328,7 @@ impl<'tcx> Body<'tcx> { &mut self, ) -> (&mut IndexVec>, &mut LocalDecls<'tcx>) { self.predecessor_cache.invalidate(); + self.is_cyclic.invalidate(); (&mut self.basic_blocks, &mut self.local_decls) } @@ -334,13 +341,14 @@ impl<'tcx> Body<'tcx> { &mut Vec>, ) { self.predecessor_cache.invalidate(); + self.is_cyclic.invalidate(); (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) } /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the /// `START_BLOCK`. pub fn is_cfg_cyclic(&self) -> bool { - graph::is_cyclic(self) + self.is_cyclic.is_cyclic(self) } #[inline] @@ -954,8 +962,7 @@ impl<'tcx> LocalDecl<'tcx> { opt_ty_info: _, opt_match_place: _, pat_span: _, - }) - | BindingForm::ImplicitSelf(ImplicitSelfKind::Imm), + }) | BindingForm::ImplicitSelf(ImplicitSelfKind::Imm), ))) ) } @@ -972,8 +979,7 @@ impl<'tcx> LocalDecl<'tcx> { opt_ty_info: _, opt_match_place: _, pat_span: _, - }) - | BindingForm::ImplicitSelf(_), + }) | BindingForm::ImplicitSelf(_), ))) ) } @@ -1737,18 +1743,14 @@ impl<'tcx> Place<'tcx> { /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or /// a single deref of a local. - // - // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? + #[inline(always)] pub fn local_or_deref_local(&self) -> Option { - match self.as_ref() { - PlaceRef { local, projection: [] } - | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), - _ => None, - } + self.as_ref().local_or_deref_local() } /// If this place represents a local variable like `_X` with no /// projections, return `Some(_X)`. + #[inline(always)] pub fn as_local(&self) -> Option { self.as_ref().as_local() } @@ -1762,6 +1764,7 @@ impl<'tcx> Place<'tcx> { /// As a concrete example, given the place a.b.c, this would yield: /// - (a, .b) /// - (a.b, .c) + /// /// Given a place without projections, the iterator is empty. pub fn iter_projections( self, @@ -1782,8 +1785,6 @@ impl From for Place<'_> { impl<'tcx> PlaceRef<'tcx> { /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or /// a single deref of a local. - // - // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? pub fn local_or_deref_local(&self) -> Option { match *self { PlaceRef { local, projection: [] } @@ -1800,6 +1801,14 @@ impl<'tcx> PlaceRef<'tcx> { _ => None, } } + + pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> { + if let &[ref proj_base @ .., elem] = self.projection { + Some((PlaceRef { local: self.local, projection: proj_base }, elem)) + } else { + None + } + } } impl Debug for Place<'_> { diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 1e70f76050..eb13c89544 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -1,4 +1,4 @@ -use crate::dep_graph::{DepConstructor, DepNode, WorkProduct, WorkProductId}; +use crate::dep_graph::{DepNode, WorkProduct, WorkProductId}; use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::ty::{subst::InternalSubsts, Instance, InstanceDef, SymbolName, TyCtxt}; use rustc_attr::InlineAttr; @@ -216,6 +216,7 @@ impl<'tcx> fmt::Display for MonoItem<'tcx> { } } +#[derive(Debug)] pub struct CodegenUnit<'tcx> { /// A name for this CGU. Incremental compilation requires that /// name be unique amongst **all** crates. Therefore, it should @@ -362,7 +363,7 @@ impl<'tcx> CodegenUnit<'tcx> { } pub fn codegen_dep_node(&self, tcx: TyCtxt<'tcx>) -> DepNode { - DepConstructor::CompileCodegenUnit(tcx, self.name()) + crate::dep_graph::make_compile_codegen_unit(tcx, self.name()) } } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 89a93096f1..c293fbe4ef 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -17,7 +17,7 @@ use std::fmt::{self, Debug}; use super::{Field, SourceInfo}; -#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] pub enum UnsafetyViolationKind { /// Only permitted in regular `fn`s, prohibited in `const fn`s. General, @@ -36,7 +36,7 @@ pub enum UnsafetyViolationKind { UnsafeFnBorrowPacked, } -#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] pub enum UnsafetyViolationDetails { CallToUnsafeFunction, UseOfInlineAssembly, @@ -121,7 +121,7 @@ impl UnsafetyViolationDetails { } } -#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] pub struct UnsafetyViolation { pub source_info: SourceInfo, pub lint_root: hir::HirId, @@ -129,7 +129,7 @@ pub struct UnsafetyViolation { pub details: UnsafetyViolationDetails, } -#[derive(Clone, TyEncodable, TyDecodable, HashStable)] +#[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)] pub struct UnsafetyCheckResult { /// Violations that are propagated *upwards* from this function. pub violations: Lrc<[UnsafetyViolation]>, @@ -439,17 +439,26 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn optimized_mir_opt_const_arg( + pub fn optimized_mir_or_const_arg_mir( self, def: ty::WithOptConstParam, ) -> &'tcx Body<'tcx> { if let Some((did, param_did)) = def.as_const_arg() { - self.optimized_mir_of_const_arg((did, param_did)) + self.mir_for_ctfe_of_const_arg((did, param_did)) } else { self.optimized_mir(def.did) } } + #[inline] + pub fn mir_for_ctfe_opt_const_arg(self, def: ty::WithOptConstParam) -> &'tcx Body<'tcx> { + if let Some((did, param_did)) = def.as_const_arg() { + self.mir_for_ctfe_of_const_arg((did, param_did)) + } else { + self.mir_for_ctfe(def.did) + } + } + #[inline] pub fn mir_abstract_const_opt_const_arg( self, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index e281010eb0..023555d91c 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -306,13 +306,13 @@ macro_rules! make_mir_visitor { let mut index = 0; for statement in statements { - let location = Location { block: block, statement_index: index }; + let location = Location { block, statement_index: index }; self.visit_statement(statement, location); index += 1; } if let Some(terminator) = terminator { - let location = Location { block: block, statement_index: index }; + let location = Location { block, statement_index: index }; self.visit_terminator(terminator, location); } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 1b5f7a2c12..ca528b2f09 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -35,1618 +35,1591 @@ fn describe_as_module(def_id: LocalDefId, tcx: TyCtxt<'_>) -> String { // Queries marked with `fatal_cycle` do not need the latter implementation, // as they will raise an fatal error on query cycles instead. rustc_queries! { - Other { - query trigger_delay_span_bug(key: DefId) -> () { - desc { "trigger a delay span bug" } - } + query trigger_delay_span_bug(key: DefId) -> () { + desc { "trigger a delay span bug" } } - Other { - /// Represents crate as a whole (as distinct from the top-level crate module). - /// If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`), - /// we will have to assume that any change means that you need to be recompiled. - /// This is because the `hir_crate` query gives you access to all other items. - /// To avoid this fate, do not call `tcx.hir().krate()`; instead, - /// prefer wrappers like `tcx.visit_all_items_in_krate()`. - query hir_crate(key: CrateNum) -> &'tcx Crate<'tcx> { - eval_always - no_hash - desc { "get the crate HIR" } - } + /// Represents crate as a whole (as distinct from the top-level crate module). + /// If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`), + /// we will have to assume that any change means that you need to be recompiled. + /// This is because the `hir_crate` query gives you access to all other items. + /// To avoid this fate, do not call `tcx.hir().krate()`; instead, + /// prefer wrappers like `tcx.visit_all_items_in_krate()`. + query hir_crate(key: CrateNum) -> &'tcx Crate<'tcx> { + eval_always + no_hash + desc { "get the crate HIR" } + } - /// The indexed HIR. This can be conveniently accessed by `tcx.hir()`. - /// Avoid calling this query directly. - query index_hir(_: CrateNum) -> &'tcx map::IndexedHir<'tcx> { - eval_always - no_hash - desc { "index HIR" } - } + /// The indexed HIR. This can be conveniently accessed by `tcx.hir()`. + /// Avoid calling this query directly. + query index_hir(_: CrateNum) -> &'tcx map::IndexedHir<'tcx> { + eval_always + no_hash + desc { "index HIR" } + } - /// The items in a module. - /// - /// This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`. - /// Avoid calling this query directly. - query hir_module_items(key: LocalDefId) -> &'tcx hir::ModuleItems { - eval_always - desc { |tcx| "HIR module items in `{}`", tcx.def_path_str(key.to_def_id()) } - } + /// The items in a module. + /// + /// This can be conveniently accessed by `tcx.hir().visit_item_likes_in_module`. + /// Avoid calling this query directly. + query hir_module_items(key: LocalDefId) -> &'tcx hir::ModuleItems { + eval_always + desc { |tcx| "HIR module items in `{}`", tcx.def_path_str(key.to_def_id()) } + } - /// Gives access to the HIR node for the HIR owner `key`. - /// - /// This can be conveniently accessed by methods on `tcx.hir()`. - /// Avoid calling this query directly. - query hir_owner(key: LocalDefId) -> Option<&'tcx crate::hir::Owner<'tcx>> { - eval_always - desc { |tcx| "HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) } - } + /// Gives access to the HIR node for the HIR owner `key`. + /// + /// This can be conveniently accessed by methods on `tcx.hir()`. + /// Avoid calling this query directly. + query hir_owner(key: LocalDefId) -> Option<&'tcx crate::hir::Owner<'tcx>> { + eval_always + desc { |tcx| "HIR owner of `{}`", tcx.def_path_str(key.to_def_id()) } + } - /// Gives access to the HIR nodes and bodies inside the HIR owner `key`. - /// - /// This can be conveniently accessed by methods on `tcx.hir()`. - /// Avoid calling this query directly. - query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx crate::hir::OwnerNodes<'tcx>> { - eval_always - desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } - } + /// Gives access to the HIR nodes and bodies inside the HIR owner `key`. + /// + /// This can be conveniently accessed by methods on `tcx.hir()`. + /// Avoid calling this query directly. + query hir_owner_nodes(key: LocalDefId) -> Option<&'tcx crate::hir::OwnerNodes<'tcx>> { + eval_always + desc { |tcx| "HIR owner items in `{}`", tcx.def_path_str(key.to_def_id()) } + } - /// Computes the `DefId` of the corresponding const parameter in case the `key` is a - /// const argument and returns `None` otherwise. - /// - /// ```ignore (incomplete) - /// let a = foo::<7>(); - /// // ^ Calling `opt_const_param_of` for this argument, - /// - /// fn foo() - /// // ^ returns this `DefId`. - /// - /// fn bar() { - /// // ^ While calling `opt_const_param_of` for other bodies returns `None`. - /// } - /// ``` - // It looks like caching this query on disk actually slightly - // worsened performance in #74376. - // - // Once const generics are more prevalently used, we might want to - // consider only caching calls returning `Some`. - query opt_const_param_of(key: LocalDefId) -> Option { - desc { |tcx| "computing the optional const parameter of `{}`", tcx.def_path_str(key.to_def_id()) } - } + /// Computes the `DefId` of the corresponding const parameter in case the `key` is a + /// const argument and returns `None` otherwise. + /// + /// ```ignore (incomplete) + /// let a = foo::<7>(); + /// // ^ Calling `opt_const_param_of` for this argument, + /// + /// fn foo() + /// // ^ returns this `DefId`. + /// + /// fn bar() { + /// // ^ While calling `opt_const_param_of` for other bodies returns `None`. + /// } + /// ``` + // It looks like caching this query on disk actually slightly + // worsened performance in #74376. + // + // Once const generics are more prevalently used, we might want to + // consider only caching calls returning `Some`. + query opt_const_param_of(key: LocalDefId) -> Option { + desc { |tcx| "computing the optional const parameter of `{}`", tcx.def_path_str(key.to_def_id()) } + } - /// Records the type of every item. - query type_of(key: DefId) -> Ty<'tcx> { - desc { |tcx| "computing type of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } + /// Records the type of every item. + query type_of(key: DefId) -> Ty<'tcx> { + desc { |tcx| "computing type of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } - query analysis(key: CrateNum) -> Result<(), ErrorReported> { - eval_always - desc { "running analysis passes on this crate" } - } + query analysis(key: CrateNum) -> Result<(), ErrorReported> { + eval_always + desc { "running analysis passes on this crate" } + } - /// Maps from the `DefId` of an item (trait/struct/enum/fn) to its - /// associated generics. - query generics_of(key: DefId) -> ty::Generics { - desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) } - storage(ArenaCacheSelector<'tcx>) - cache_on_disk_if { key.is_local() } - load_cached(tcx, id) { - let generics: Option = tcx.queries.on_disk_cache.as_ref() - .and_then(|c| c.try_load_query_result(tcx, id)); - generics - } + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to its + /// associated generics. + query generics_of(key: DefId) -> ty::Generics { + desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + load_cached(tcx, id) { + let generics: Option = tcx.queries.on_disk_cache.as_ref() + .and_then(|c| c.try_load_query_result(tcx, id)); + generics } + } - /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the - /// predicates (where-clauses) that must be proven true in order - /// to reference it. This is almost always the "predicates query" - /// that you want. - /// - /// `predicates_of` builds on `predicates_defined_on` -- in fact, - /// it is almost always the same as that query, except for the - /// case of traits. For traits, `predicates_of` contains - /// an additional `Self: Trait<...>` predicate that users don't - /// actually write. This reflects the fact that to invoke the - /// trait (e.g., via `Default::default`) you must supply types - /// that actually implement the trait. (However, this extra - /// predicate gets in the way of some checks, which are intended - /// to operate over only the actual where-clauses written by the - /// user.) - query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the + /// predicates (where-clauses) that must be proven true in order + /// to reference it. This is almost always the "predicates query" + /// that you want. + /// + /// `predicates_of` builds on `predicates_defined_on` -- in fact, + /// it is almost always the same as that query, except for the + /// case of traits. For traits, `predicates_of` contains + /// an additional `Self: Trait<...>` predicate that users don't + /// actually write. This reflects the fact that to invoke the + /// trait (e.g., via `Default::default`) you must supply types + /// that actually implement the trait. (However, this extra + /// predicate gets in the way of some checks, which are intended + /// to operate over only the actual where-clauses written by the + /// user.) + query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } - /// Returns the list of bounds that can be used for - /// `SelectionCandidate::ProjectionCandidate(_)` and - /// `ProjectionTyCandidate::TraitDef`. - /// Specifically this is the bounds written on the trait's type - /// definition, or those after the `impl` keyword - /// - /// ```ignore (incomplete) - /// type X: Bound + 'lt - /// // ^^^^^^^^^^^ - /// impl Debug + Display - /// // ^^^^^^^^^^^^^^^ - /// ``` - /// - /// `key` is the `DefId` of the associated type or opaque type. - /// - /// Bounds from the parent (e.g. with nested impl trait) are not included. - query explicit_item_bounds(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { - desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } - } + /// Returns the list of bounds that can be used for + /// `SelectionCandidate::ProjectionCandidate(_)` and + /// `ProjectionTyCandidate::TraitDef`. + /// Specifically this is the bounds written on the trait's type + /// definition, or those after the `impl` keyword + /// + /// ```ignore (incomplete) + /// type X: Bound + 'lt + /// // ^^^^^^^^^^^ + /// impl Debug + Display + /// // ^^^^^^^^^^^^^^^ + /// ``` + /// + /// `key` is the `DefId` of the associated type or opaque type. + /// + /// Bounds from the parent (e.g. with nested impl trait) are not included. + query explicit_item_bounds(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + } - /// Elaborated version of the predicates from `explicit_item_bounds`. - /// - /// For example: - /// - /// ``` - /// trait MyTrait { - /// type MyAType: Eq + ?Sized; - /// } - /// ``` - /// - /// `explicit_item_bounds` returns `[::MyAType: Eq]`, - /// and `item_bounds` returns - /// ```text - /// [ - /// ::MyAType: Eq, - /// ::MyAType: PartialEq<::MyAType> - /// ] - /// ``` - /// - /// Bounds from the parent (e.g. with nested impl trait) are not included. - query item_bounds(key: DefId) -> &'tcx ty::List> { - desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } - } + /// Elaborated version of the predicates from `explicit_item_bounds`. + /// + /// For example: + /// + /// ``` + /// trait MyTrait { + /// type MyAType: Eq + ?Sized; + /// } + /// ``` + /// + /// `explicit_item_bounds` returns `[::MyAType: Eq]`, + /// and `item_bounds` returns + /// ```text + /// [ + /// ::MyAType: Eq, + /// ::MyAType: PartialEq<::MyAType> + /// ] + /// ``` + /// + /// Bounds from the parent (e.g. with nested impl trait) are not included. + query item_bounds(key: DefId) -> &'tcx ty::List> { + desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } + } - query projection_ty_from_predicates(key: (DefId, DefId)) -> Option> { - desc { |tcx| "finding projection type inside predicates of `{}`", tcx.def_path_str(key.0) } - } + query projection_ty_from_predicates(key: (DefId, DefId)) -> Option> { + desc { |tcx| "finding projection type inside predicates of `{}`", tcx.def_path_str(key.0) } + } - query native_libraries(_: CrateNum) -> Lrc> { - desc { "looking up the native libraries of a linked crate" } - } + query native_libraries(_: CrateNum) -> Lrc> { + desc { "looking up the native libraries of a linked crate" } + } - query lint_levels(_: CrateNum) -> LintLevelMap { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "computing the lint levels for items in this crate" } - } + query lint_levels(_: CrateNum) -> LintLevelMap { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "computing the lint levels for items in this crate" } + } - query parent_module_from_def_id(key: LocalDefId) -> LocalDefId { - eval_always - desc { |tcx| "parent module of `{}`", tcx.def_path_str(key.to_def_id()) } - } + query parent_module_from_def_id(key: LocalDefId) -> LocalDefId { + eval_always + desc { |tcx| "parent module of `{}`", tcx.def_path_str(key.to_def_id()) } + } - /// Internal helper query. Use `tcx.expansion_that_defined` instead - query expn_that_defined(key: DefId) -> rustc_span::ExpnId { - desc { |tcx| "expansion that defined `{}`", tcx.def_path_str(key) } - } + /// Internal helper query. Use `tcx.expansion_that_defined` instead + query expn_that_defined(key: DefId) -> rustc_span::ExpnId { + desc { |tcx| "expansion that defined `{}`", tcx.def_path_str(key) } } - Codegen { - query is_panic_runtime(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate is_panic_runtime" } - } + query is_panic_runtime(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate is_panic_runtime" } } - Codegen { - /// Set of all the `DefId`s in this crate that have MIR associated with - /// them. This includes all the body owners, but also things like struct - /// constructors. - query mir_keys(_: CrateNum) -> FxHashSet { - storage(ArenaCacheSelector<'tcx>) - desc { "getting a list of all mir_keys" } - } + /// Set of all the `DefId`s in this crate that have MIR associated with + /// them. This includes all the body owners, but also things like struct + /// constructors. + query mir_keys(_: CrateNum) -> FxHashSet { + storage(ArenaCacheSelector<'tcx>) + desc { "getting a list of all mir_keys" } + } - /// Maps DefId's that have an associated `mir::Body` to the result - /// of the MIR const-checking pass. This is the set of qualifs in - /// the final value of a `const`. - query mir_const_qualif(key: DefId) -> mir::ConstQualifs { - desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - query mir_const_qualif_const_arg( - key: (LocalDefId, DefId) - ) -> mir::ConstQualifs { - desc { - |tcx| "const checking the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()) - } + /// Maps DefId's that have an associated `mir::Body` to the result + /// of the MIR const-checking pass. This is the set of qualifs in + /// the final value of a `const`. + query mir_const_qualif(key: DefId) -> mir::ConstQualifs { + desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + query mir_const_qualif_const_arg( + key: (LocalDefId, DefId) + ) -> mir::ConstQualifs { + desc { + |tcx| "const checking the const argument `{}`", + tcx.def_path_str(key.0.to_def_id()) } + } - /// Fetch the MIR for a given `DefId` right after it's built - this includes - /// unreachable code. - query mir_built(key: ty::WithOptConstParam) -> &'tcx Steal> { - desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key.did.to_def_id()) } - } + /// Fetch the MIR for a given `DefId` right after it's built - this includes + /// unreachable code. + query mir_built(key: ty::WithOptConstParam) -> &'tcx Steal> { + desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key.did.to_def_id()) } + } - /// Fetch the MIR for a given `DefId` up till the point where it is - /// ready for const qualification. - /// - /// See the README for the `mir` module for details. - query mir_const(key: ty::WithOptConstParam) -> &'tcx Steal> { - desc { - |tcx| "processing MIR for {}`{}`", - if key.const_param_did.is_some() { "the const argument " } else { "" }, - tcx.def_path_str(key.did.to_def_id()), - } - no_hash - } + /// Fetch the MIR for a given `DefId` up till the point where it is + /// ready for const qualification. + /// + /// See the README for the `mir` module for details. + query mir_const(key: ty::WithOptConstParam) -> &'tcx Steal> { + desc { + |tcx| "processing MIR for {}`{}`", + if key.const_param_did.is_some() { "the const argument " } else { "" }, + tcx.def_path_str(key.did.to_def_id()), + } + no_hash + } - /// Try to build an abstract representation of the given constant. - query mir_abstract_const( - key: DefId - ) -> Result]>, ErrorReported> { - desc { - |tcx| "building an abstract representation for {}", tcx.def_path_str(key), - } + /// Try to build an abstract representation of the given constant. + query mir_abstract_const( + key: DefId + ) -> Result]>, ErrorReported> { + desc { + |tcx| "building an abstract representation for {}", tcx.def_path_str(key), } - /// Try to build an abstract representation of the given constant. - query mir_abstract_const_of_const_arg( - key: (LocalDefId, DefId) - ) -> Result]>, ErrorReported> { - desc { - |tcx| - "building an abstract representation for the const argument {}", - tcx.def_path_str(key.0.to_def_id()), - } + } + /// Try to build an abstract representation of the given constant. + query mir_abstract_const_of_const_arg( + key: (LocalDefId, DefId) + ) -> Result]>, ErrorReported> { + desc { + |tcx| + "building an abstract representation for the const argument {}", + tcx.def_path_str(key.0.to_def_id()), } + } - query try_unify_abstract_consts(key: ( - (ty::WithOptConstParam, SubstsRef<'tcx>), - (ty::WithOptConstParam, SubstsRef<'tcx>) - )) -> bool { - desc { - |tcx| "trying to unify the generic constants {} and {}", - tcx.def_path_str(key.0.0.did), tcx.def_path_str(key.1.0.did) - } + query try_unify_abstract_consts(key: ( + (ty::WithOptConstParam, SubstsRef<'tcx>), + (ty::WithOptConstParam, SubstsRef<'tcx>) + )) -> bool { + desc { + |tcx| "trying to unify the generic constants {} and {}", + tcx.def_path_str(key.0.0.did), tcx.def_path_str(key.1.0.did) } + } - query mir_drops_elaborated_and_const_checked( - key: ty::WithOptConstParam - ) -> &'tcx Steal> { - no_hash - desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.did.to_def_id()) } - } + query mir_drops_elaborated_and_const_checked( + key: ty::WithOptConstParam + ) -> &'tcx Steal> { + no_hash + desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.did.to_def_id()) } + } - query mir_promoted(key: ty::WithOptConstParam) -> - ( - &'tcx Steal>, - &'tcx Steal>> - ) { - no_hash - desc { - |tcx| "processing {}`{}`", - if key.const_param_did.is_some() { "the const argument " } else { "" }, - tcx.def_path_str(key.did.to_def_id()), - } - } + query mir_for_ctfe( + key: DefId + ) -> &'tcx mir::Body<'tcx> { + desc { |tcx| "caching mir of `{}` for CTFE", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } - /// MIR after our optimization passes have run. This is MIR that is ready - /// for codegen. This is also the only query that can fetch non-local MIR, at present. - query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { - desc { |tcx| "optimizing MIR for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - query optimized_mir_of_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::Body<'tcx> { - desc { - |tcx| "optimizing MIR for the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()) - } + query mir_for_ctfe_of_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::Body<'tcx> { + desc { + |tcx| "MIR for CTFE of the const argument `{}`", + tcx.def_path_str(key.0.to_def_id()) } + } - /// Returns coverage summary info for a function, after executing the `InstrumentCoverage` - /// MIR pass (assuming the -Zinstrument-coverage option is enabled). - query coverageinfo(key: DefId) -> mir::CoverageInfo { - desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key) } - storage(ArenaCacheSelector<'tcx>) - cache_on_disk_if { key.is_local() } + query mir_promoted(key: ty::WithOptConstParam) -> + ( + &'tcx Steal>, + &'tcx Steal>> + ) { + no_hash + desc { + |tcx| "processing {}`{}`", + if key.const_param_did.is_some() { "the const argument " } else { "" }, + tcx.def_path_str(key.did.to_def_id()), } + } - /// Returns the name of the file that contains the function body, if instrumented for coverage. - query covered_file_name(key: DefId) -> Option { - desc { |tcx| "retrieving the covered file name, if instrumented, for `{}`", tcx.def_path_str(key) } - storage(ArenaCacheSelector<'tcx>) - cache_on_disk_if { key.is_local() } - } + /// MIR after our optimization passes have run. This is MIR that is ready + /// for codegen. This is also the only query that can fetch non-local MIR, at present. + query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> { + desc { |tcx| "optimizing MIR for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } - /// Returns the `CodeRegions` for a function that has instrumented coverage, in case the - /// function was optimized out before codegen, and before being added to the Coverage Map. - query covered_code_regions(key: DefId) -> Vec<&'tcx mir::coverage::CodeRegion> { - desc { |tcx| "retrieving the covered `CodeRegion`s, if instrumented, for `{}`", tcx.def_path_str(key) } - storage(ArenaCacheSelector<'tcx>) - cache_on_disk_if { key.is_local() } - } + /// Returns coverage summary info for a function, after executing the `InstrumentCoverage` + /// MIR pass (assuming the -Zinstrument-coverage option is enabled). + query coverageinfo(key: DefId) -> mir::CoverageInfo { + desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + } - /// The `DefId` is the `DefId` of the containing MIR body. Promoteds do not have their own - /// `DefId`. This function returns all promoteds in the specified body. The body references - /// promoteds by the `DefId` and the `mir::Promoted` index. This is necessary, because - /// after inlining a body may refer to promoteds from other bodies. In that case you still - /// need to use the `DefId` of the original body. - query promoted_mir(key: DefId) -> &'tcx IndexVec> { - desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } - query promoted_mir_of_const_arg( - key: (LocalDefId, DefId) - ) -> &'tcx IndexVec> { - desc { - |tcx| "optimizing promoted MIR for the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()), - } - } + /// Returns the name of the file that contains the function body, if instrumented for coverage. + query covered_file_name(key: DefId) -> Option { + desc { |tcx| "retrieving the covered file name, if instrumented, for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } } - TypeChecking { - /// Erases regions from `ty` to yield a new type. - /// Normally you would just use `tcx.erase_regions(value)`, - /// however, which uses this query as a kind of cache. - query erase_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { - // This query is not expected to have input -- as a result, it - // is not a good candidates for "replay" because it is essentially a - // pure function of its input (and hence the expectation is that - // no caller would be green **apart** from just these - // queries). Making it anonymous avoids hashing the result, which - // may save a bit of time. - anon - desc { "erasing regions from `{:?}`", ty } - } + /// Returns the `CodeRegions` for a function that has instrumented coverage, in case the + /// function was optimized out before codegen, and before being added to the Coverage Map. + query covered_code_regions(key: DefId) -> Vec<&'tcx mir::coverage::CodeRegion> { + desc { |tcx| "retrieving the covered `CodeRegion`s, if instrumented, for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } } - Linking { - query wasm_import_module_map(_: CrateNum) -> FxHashMap { - storage(ArenaCacheSelector<'tcx>) - desc { "wasm import module map" } + /// The `DefId` is the `DefId` of the containing MIR body. Promoteds do not have their own + /// `DefId`. This function returns all promoteds in the specified body. The body references + /// promoteds by the `DefId` and the `mir::Promoted` index. This is necessary, because + /// after inlining a body may refer to promoteds from other bodies. In that case you still + /// need to use the `DefId` of the original body. + query promoted_mir(key: DefId) -> &'tcx IndexVec> { + desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + query promoted_mir_of_const_arg( + key: (LocalDefId, DefId) + ) -> &'tcx IndexVec> { + desc { + |tcx| "optimizing promoted MIR for the const argument `{}`", + tcx.def_path_str(key.0.to_def_id()), } } - Other { - /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the - /// predicates (where-clauses) directly defined on it. This is - /// equal to the `explicit_predicates_of` predicates plus the - /// `inferred_outlives_of` predicates. - query predicates_defined_on(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } - } + /// Erases regions from `ty` to yield a new type. + /// Normally you would just use `tcx.erase_regions(value)`, + /// however, which uses this query as a kind of cache. + query erase_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { + // This query is not expected to have input -- as a result, it + // is not a good candidates for "replay" because it is essentially a + // pure function of its input (and hence the expectation is that + // no caller would be green **apart** from just these + // queries). Making it anonymous avoids hashing the result, which + // may save a bit of time. + anon + desc { "erasing regions from `{:?}`", ty } + } - /// Returns everything that looks like a predicate written explicitly - /// by the user on a trait item. - /// - /// Traits are unusual, because predicates on associated types are - /// converted into bounds on that type for backwards compatibility: - /// - /// trait X where Self::U: Copy { type U; } - /// - /// becomes - /// - /// trait X { type U: Copy; } - /// - /// `explicit_predicates_of` and `explicit_item_bounds` will then take - /// the appropriate subsets of the predicates here. - query trait_explicit_predicates_and_bounds(key: LocalDefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key.to_def_id()) } - } + query wasm_import_module_map(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { "wasm import module map" } + } - /// Returns the predicates written explicitly by the user. - query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } - } + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the + /// predicates (where-clauses) directly defined on it. This is + /// equal to the `explicit_predicates_of` predicates plus the + /// `inferred_outlives_of` predicates. + query predicates_defined_on(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } + } - /// Returns the inferred outlives predicates (e.g., for `struct - /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`). - query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { - desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) } - } + /// Returns everything that looks like a predicate written explicitly + /// by the user on a trait item. + /// + /// Traits are unusual, because predicates on associated types are + /// converted into bounds on that type for backwards compatibility: + /// + /// trait X where Self::U: Copy { type U; } + /// + /// becomes + /// + /// trait X { type U: Copy; } + /// + /// `explicit_predicates_of` and `explicit_item_bounds` will then take + /// the appropriate subsets of the predicates here. + query trait_explicit_predicates_and_bounds(key: LocalDefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key.to_def_id()) } + } - /// Maps from the `DefId` of a trait to the list of - /// super-predicates. This is a subset of the full list of - /// predicates. We store these in a separate map because we must - /// evaluate them even during type conversion, often before the - /// full predicates are available (note that supertraits have - /// additional acyclicity requirements). - query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) } - } + /// Returns the predicates written explicitly by the user. + query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } + } - /// To avoid cycles within the predicates of a single item we compute - /// per-type-parameter predicates for resolving `T::AssocTy`. - query type_param_predicates(key: (DefId, LocalDefId)) -> ty::GenericPredicates<'tcx> { - desc { |tcx| "computing the bounds for type parameter `{}`", { - let id = tcx.hir().local_def_id_to_hir_id(key.1); - tcx.hir().ty_param_name(id) - }} - } + /// Returns the inferred outlives predicates (e.g., for `struct + /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`). + query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) } + } - query trait_def(key: DefId) -> ty::TraitDef { - desc { |tcx| "computing trait definition for `{}`", tcx.def_path_str(key) } - storage(ArenaCacheSelector<'tcx>) - } - query adt_def(key: DefId) -> &'tcx ty::AdtDef { - desc { |tcx| "computing ADT definition for `{}`", tcx.def_path_str(key) } - } - query adt_destructor(key: DefId) -> Option { - desc { |tcx| "computing `Drop` impl for `{}`", tcx.def_path_str(key) } - } + /// Maps from the `DefId` of a trait to the list of + /// super-predicates. This is a subset of the full list of + /// predicates. We store these in a separate map because we must + /// evaluate them even during type conversion, often before the + /// full predicates are available (note that supertraits have + /// additional acyclicity requirements). + query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) } + } - // The cycle error here should be reported as an error by `check_representable`. - // We consider the type as Sized in the meanwhile to avoid - // further errors (done in impl Value for AdtSizedConstraint). - // Use `cycle_delay_bug` to delay the cycle error here to be emitted later - // in case we accidentally otherwise don't emit an error. - query adt_sized_constraint( - key: DefId - ) -> AdtSizedConstraint<'tcx> { - desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) } - cycle_delay_bug - } + /// To avoid cycles within the predicates of a single item we compute + /// per-type-parameter predicates for resolving `T::AssocTy`. + query type_param_predicates(key: (DefId, LocalDefId)) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing the bounds for type parameter `{}`", { + let id = tcx.hir().local_def_id_to_hir_id(key.1); + tcx.hir().ty_param_name(id) + }} + } - query adt_dtorck_constraint( - key: DefId - ) -> Result, NoSolution> { - desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } - } + query trait_def(key: DefId) -> ty::TraitDef { + desc { |tcx| "computing trait definition for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + } + query adt_def(key: DefId) -> &'tcx ty::AdtDef { + desc { |tcx| "computing ADT definition for `{}`", tcx.def_path_str(key) } + } + query adt_destructor(key: DefId) -> Option { + desc { |tcx| "computing `Drop` impl for `{}`", tcx.def_path_str(key) } + } - /// Returns `true` if this is a const fn, use the `is_const_fn` to know whether your crate - /// actually sees it as const fn (e.g., the const-fn-ness might be unstable and you might - /// not have the feature gate active). - /// - /// **Do not call this function manually.** It is only meant to cache the base data for the - /// `is_const_fn` function. - query is_const_fn_raw(key: DefId) -> bool { - desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) } - } + // The cycle error here should be reported as an error by `check_representable`. + // We consider the type as Sized in the meanwhile to avoid + // further errors (done in impl Value for AdtSizedConstraint). + // Use `cycle_delay_bug` to delay the cycle error here to be emitted later + // in case we accidentally otherwise don't emit an error. + query adt_sized_constraint( + key: DefId + ) -> AdtSizedConstraint<'tcx> { + desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) } + cycle_delay_bug + } - /// Returns `true` if this is a const `impl`. **Do not call this function manually.** - /// - /// This query caches the base data for the `is_const_impl` helper function, which also - /// takes into account stability attributes (e.g., `#[rustc_const_unstable]`). - query is_const_impl_raw(key: DefId) -> bool { - desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) } - } + query adt_dtorck_constraint( + key: DefId + ) -> Result, NoSolution> { + desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } + } - query asyncness(key: DefId) -> hir::IsAsync { - desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } - } + /// Returns `true` if this is a const fn, use the `is_const_fn` to know whether your crate + /// actually sees it as const fn (e.g., the const-fn-ness might be unstable and you might + /// not have the feature gate active). + /// + /// **Do not call this function manually.** It is only meant to cache the base data for the + /// `is_const_fn` function. + query is_const_fn_raw(key: DefId) -> bool { + desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) } + } - /// Returns `true` if calls to the function may be promoted. - /// - /// This is either because the function is e.g., a tuple-struct or tuple-variant - /// constructor, or because it has the `#[rustc_promotable]` attribute. The attribute should - /// be removed in the future in favour of some form of check which figures out whether the - /// function does not inspect the bits of any of its arguments (so is essentially just a - /// constructor function). - query is_promotable_const_fn(key: DefId) -> bool { - desc { |tcx| "checking if item is promotable: `{}`", tcx.def_path_str(key) } - } + /// Returns `true` if this is a const `impl`. **Do not call this function manually.** + /// + /// This query caches the base data for the `is_const_impl` helper function, which also + /// takes into account stability attributes (e.g., `#[rustc_const_unstable]`). + query is_const_impl_raw(key: DefId) -> bool { + desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) } + } - /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). - query is_foreign_item(key: DefId) -> bool { - desc { |tcx| "checking if `{}` is a foreign item", tcx.def_path_str(key) } - } + query asyncness(key: DefId) -> hir::IsAsync { + desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } + } - /// Returns `Some(mutability)` if the node pointed to by `def_id` is a static item. - query static_mutability(def_id: DefId) -> Option { - desc { |tcx| "looking up static mutability of `{}`", tcx.def_path_str(def_id) } - } + /// Returns `true` if calls to the function may be promoted. + /// + /// This is either because the function is e.g., a tuple-struct or tuple-variant + /// constructor, or because it has the `#[rustc_promotable]` attribute. The attribute should + /// be removed in the future in favour of some form of check which figures out whether the + /// function does not inspect the bits of any of its arguments (so is essentially just a + /// constructor function). + query is_promotable_const_fn(key: DefId) -> bool { + desc { |tcx| "checking if item is promotable: `{}`", tcx.def_path_str(key) } + } - /// Returns `Some(generator_kind)` if the node pointed to by `def_id` is a generator. - query generator_kind(def_id: DefId) -> Option { - desc { |tcx| "looking up generator kind of `{}`", tcx.def_path_str(def_id) } - } + /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). + query is_foreign_item(key: DefId) -> bool { + desc { |tcx| "checking if `{}` is a foreign item", tcx.def_path_str(key) } + } - /// Gets a map with the variance of every item; use `item_variance` instead. - query crate_variances(_: CrateNum) -> ty::CrateVariancesMap<'tcx> { - storage(ArenaCacheSelector<'tcx>) - desc { "computing the variances for items in this crate" } - } + /// Returns `Some(mutability)` if the node pointed to by `def_id` is a static item. + query static_mutability(def_id: DefId) -> Option { + desc { |tcx| "looking up static mutability of `{}`", tcx.def_path_str(def_id) } + } - /// Maps from the `DefId` of a type or region parameter to its (inferred) variance. - query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { - desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) } - } + /// Returns `Some(generator_kind)` if the node pointed to by `def_id` is a generator. + query generator_kind(def_id: DefId) -> Option { + desc { |tcx| "looking up generator kind of `{}`", tcx.def_path_str(def_id) } } - TypeChecking { - /// Maps from thee `DefId` of a type to its (inferred) outlives. - query inferred_outlives_crate(_: CrateNum) - -> ty::CratePredicatesMap<'tcx> { - storage(ArenaCacheSelector<'tcx>) - desc { "computing the inferred outlives predicates for items in this crate" } - } + /// Gets a map with the variance of every item; use `item_variance` instead. + query crate_variances(_: CrateNum) -> ty::CrateVariancesMap<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { "computing the variances for items in this crate" } } - Other { - /// Maps from an impl/trait `DefId to a list of the `DefId`s of its items. - query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { - desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } - } + /// Maps from the `DefId` of a type or region parameter to its (inferred) variance. + query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { + desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) } + } - /// Maps from a trait item to the trait item "descriptor". - query associated_item(key: DefId) -> ty::AssocItem { - desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } - storage(ArenaCacheSelector<'tcx>) - } + /// Maps from thee `DefId` of a type to its (inferred) outlives. + query inferred_outlives_crate(_: CrateNum) + -> ty::CratePredicatesMap<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { "computing the inferred outlives predicates for items in this crate" } + } - /// Collects the associated items defined on a trait or impl. - query associated_items(key: DefId) -> ty::AssociatedItems<'tcx> { - storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } - } + /// Maps from an impl/trait `DefId to a list of the `DefId`s of its items. + query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { + desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } + } - query impl_trait_ref(key: DefId) -> Option> { - desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(key) } - } - query impl_polarity(key: DefId) -> ty::ImplPolarity { - desc { |tcx| "computing implementation polarity of `{}`", tcx.def_path_str(key) } - } + /// Maps from a trait item to the trait item "descriptor". + query associated_item(key: DefId) -> ty::AssocItem { + desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + } - query issue33140_self_ty(key: DefId) -> Option> { - desc { |tcx| "computing Self type wrt issue #33140 `{}`", tcx.def_path_str(key) } - } + /// Collects the associated items defined on a trait or impl. + query associated_items(key: DefId) -> ty::AssociatedItems<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } } - TypeChecking { - /// Maps a `DefId` of a type to a list of its inherent impls. - /// Contains implementations of methods that are inherent to a type. - /// Methods in these implementations don't need to be exported. - query inherent_impls(key: DefId) -> &'tcx [DefId] { - desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } - eval_always - } + /// Given an `impl_id`, return the trait it implements. + /// Return `None` if this is an inherent impl. + query impl_trait_ref(impl_id: DefId) -> Option> { + desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) } + } + query impl_polarity(impl_id: DefId) -> ty::ImplPolarity { + desc { |tcx| "computing implementation polarity of `{}`", tcx.def_path_str(impl_id) } } - TypeChecking { - /// The result of unsafety-checking this `LocalDefId`. - query unsafety_check_result(key: LocalDefId) -> &'tcx mir::UnsafetyCheckResult { - desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if { true } - } - query unsafety_check_result_for_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::UnsafetyCheckResult { - desc { - |tcx| "unsafety-checking the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()) - } - } + query issue33140_self_ty(key: DefId) -> Option> { + desc { |tcx| "computing Self type wrt issue #33140 `{}`", tcx.def_path_str(key) } + } - /// HACK: when evaluated, this reports a "unsafe derive on repr(packed)" error. - /// - /// Unsafety checking is executed for each method separately, but we only want - /// to emit this error once per derive. As there are some impls with multiple - /// methods, we use a query for deduplication. - query unsafe_derive_on_repr_packed(key: LocalDefId) -> () { - desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } - } + /// Maps a `DefId` of a type to a list of its inherent impls. + /// Contains implementations of methods that are inherent to a type. + /// Methods in these implementations don't need to be exported. + query inherent_impls(key: DefId) -> &'tcx [DefId] { + desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } + eval_always + } - /// The signature of functions. - query fn_sig(key: DefId) -> ty::PolyFnSig<'tcx> { - desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } + /// The result of unsafety-checking this `LocalDefId`. + query unsafety_check_result(key: LocalDefId) -> &'tcx mir::UnsafetyCheckResult { + desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + } + query unsafety_check_result_for_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::UnsafetyCheckResult { + desc { + |tcx| "unsafety-checking the const argument `{}`", + tcx.def_path_str(key.0.to_def_id()) } } - Other { - query lint_mod(key: LocalDefId) -> () { - desc { |tcx| "linting {}", describe_as_module(key, tcx) } - } + /// HACK: when evaluated, this reports a "unsafe derive on repr(packed)" error. + /// + /// Unsafety checking is executed for each method separately, but we only want + /// to emit this error once per derive. As there are some impls with multiple + /// methods, we use a query for deduplication. + query unsafe_derive_on_repr_packed(key: LocalDefId) -> () { + desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } + } - /// Checks the attributes in the module. - query check_mod_attrs(key: LocalDefId) -> () { - desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } - } + /// The signature of functions. + query fn_sig(key: DefId) -> ty::PolyFnSig<'tcx> { + desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } + } - query check_mod_unstable_api_usage(key: LocalDefId) -> () { - desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } - } + query lint_mod(key: LocalDefId) -> () { + desc { |tcx| "linting {}", describe_as_module(key, tcx) } + } - /// Checks the const bodies in the module for illegal operations (e.g. `if` or `loop`). - query check_mod_const_bodies(key: LocalDefId) -> () { - desc { |tcx| "checking consts in {}", describe_as_module(key, tcx) } - } + /// Checks the attributes in the module. + query check_mod_attrs(key: LocalDefId) -> () { + desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } + } - /// Checks the loops in the module. - query check_mod_loops(key: LocalDefId) -> () { - desc { |tcx| "checking loops in {}", describe_as_module(key, tcx) } - } + query check_mod_unstable_api_usage(key: LocalDefId) -> () { + desc { |tcx| "checking for unstable API usage in {}", describe_as_module(key, tcx) } + } - query check_mod_naked_functions(key: LocalDefId) -> () { - desc { |tcx| "checking naked functions in {}", describe_as_module(key, tcx) } - } + /// Checks the const bodies in the module for illegal operations (e.g. `if` or `loop`). + query check_mod_const_bodies(key: LocalDefId) -> () { + desc { |tcx| "checking consts in {}", describe_as_module(key, tcx) } + } - query check_mod_item_types(key: LocalDefId) -> () { - desc { |tcx| "checking item types in {}", describe_as_module(key, tcx) } - } + /// Checks the loops in the module. + query check_mod_loops(key: LocalDefId) -> () { + desc { |tcx| "checking loops in {}", describe_as_module(key, tcx) } + } - query check_mod_privacy(key: LocalDefId) -> () { - desc { |tcx| "checking privacy in {}", describe_as_module(key, tcx) } - } + query check_mod_naked_functions(key: LocalDefId) -> () { + desc { |tcx| "checking naked functions in {}", describe_as_module(key, tcx) } + } - query check_mod_intrinsics(key: LocalDefId) -> () { - desc { |tcx| "checking intrinsics in {}", describe_as_module(key, tcx) } - } + query check_mod_item_types(key: LocalDefId) -> () { + desc { |tcx| "checking item types in {}", describe_as_module(key, tcx) } + } - query check_mod_liveness(key: LocalDefId) -> () { - desc { |tcx| "checking liveness of variables in {}", describe_as_module(key, tcx) } - } + query check_mod_privacy(key: LocalDefId) -> () { + desc { |tcx| "checking privacy in {}", describe_as_module(key, tcx) } + } - query check_mod_impl_wf(key: LocalDefId) -> () { - desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) } - } + query check_mod_intrinsics(key: LocalDefId) -> () { + desc { |tcx| "checking intrinsics in {}", describe_as_module(key, tcx) } + } - query collect_mod_item_types(key: LocalDefId) -> () { - desc { |tcx| "collecting item types in {}", describe_as_module(key, tcx) } - } + query check_mod_liveness(key: LocalDefId) -> () { + desc { |tcx| "checking liveness of variables in {}", describe_as_module(key, tcx) } + } - /// Caches `CoerceUnsized` kinds for impls on custom types. - query coerce_unsized_info(key: DefId) - -> ty::adjustment::CoerceUnsizedInfo { - desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } - } + query check_mod_impl_wf(key: LocalDefId) -> () { + desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) } } - TypeChecking { - query typeck_item_bodies(_: CrateNum) -> () { - desc { "type-checking all item bodies" } - } + query collect_mod_item_types(key: LocalDefId) -> () { + desc { |tcx| "collecting item types in {}", describe_as_module(key, tcx) } + } - query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { - desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if { true } - } - query typeck_const_arg( - key: (LocalDefId, DefId) - ) -> &'tcx ty::TypeckResults<'tcx> { - desc { - |tcx| "type-checking the const argument `{}`", - tcx.def_path_str(key.0.to_def_id()), - } - } - query diagnostic_only_typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { - desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if { true } - load_cached(tcx, id) { - let typeck_results: Option> = tcx - .queries.on_disk_cache.as_ref() - .and_then(|c| c.try_load_query_result(tcx, id)); - - typeck_results.map(|x| &*tcx.arena.alloc(x)) - } + /// Caches `CoerceUnsized` kinds for impls on custom types. + query coerce_unsized_info(key: DefId) + -> ty::adjustment::CoerceUnsizedInfo { + desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } } + + query typeck_item_bodies(_: CrateNum) -> () { + desc { "type-checking all item bodies" } } - Other { - query used_trait_imports(key: LocalDefId) -> &'tcx FxHashSet { - desc { |tcx| "used_trait_imports `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if { true } + query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { + desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + } + query typeck_const_arg( + key: (LocalDefId, DefId) + ) -> &'tcx ty::TypeckResults<'tcx> { + desc { + |tcx| "type-checking the const argument `{}`", + tcx.def_path_str(key.0.to_def_id()), } } + query diagnostic_only_typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { + desc { |tcx| "type-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + load_cached(tcx, id) { + let typeck_results: Option> = tcx + .queries.on_disk_cache.as_ref() + .and_then(|c| c.try_load_query_result(tcx, id)); - TypeChecking { - query has_typeck_results(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` has a body", tcx.def_path_str(def_id) } + typeck_results.map(|x| &*tcx.arena.alloc(x)) } + } - query coherent_trait(def_id: DefId) -> () { - desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } - } + query used_trait_imports(key: LocalDefId) -> &'tcx FxHashSet { + desc { |tcx| "used_trait_imports `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } } - BorrowChecking { - /// Borrow-checks the function body. If this is a closure, returns - /// additional requirements that the closure's creator must verify. - query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> { - desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) } - cache_on_disk_if(tcx, opt_result) { - tcx.is_closure(key.to_def_id()) - || opt_result.map_or(false, |r| !r.concrete_opaque_types.is_empty()) - } + query has_typeck_results(def_id: DefId) -> bool { + desc { |tcx| "checking whether `{}` has a body", tcx.def_path_str(def_id) } + } + + query coherent_trait(def_id: DefId) -> () { + desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } + } + + /// Borrow-checks the function body. If this is a closure, returns + /// additional requirements that the closure's creator must verify. + query mir_borrowck(key: LocalDefId) -> &'tcx mir::BorrowCheckResult<'tcx> { + desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if(tcx, opt_result) { + tcx.is_closure(key.to_def_id()) + || opt_result.map_or(false, |r| !r.concrete_opaque_types.is_empty()) } - query mir_borrowck_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::BorrowCheckResult<'tcx> { - desc { - |tcx| "borrow-checking the const argument`{}`", - tcx.def_path_str(key.0.to_def_id()) - } + } + query mir_borrowck_const_arg(key: (LocalDefId, DefId)) -> &'tcx mir::BorrowCheckResult<'tcx> { + desc { + |tcx| "borrow-checking the const argument`{}`", + tcx.def_path_str(key.0.to_def_id()) } } - TypeChecking { - /// Gets a complete map from all types to their inherent impls. - /// Not meant to be used directly outside of coherence. - /// (Defined only for `LOCAL_CRATE`.) - query crate_inherent_impls(k: CrateNum) - -> CrateInherentImpls { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "all inherent impls defined in crate `{:?}`", k } - } + /// Gets a complete map from all types to their inherent impls. + /// Not meant to be used directly outside of coherence. + /// (Defined only for `LOCAL_CRATE`.) + query crate_inherent_impls(k: CrateNum) + -> CrateInherentImpls { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "all inherent impls defined in crate `{:?}`", k } + } - /// Checks all types in the crate for overlap in their inherent impls. Reports errors. - /// Not meant to be used directly outside of coherence. - /// (Defined only for `LOCAL_CRATE`.) - query crate_inherent_impls_overlap_check(_: CrateNum) - -> () { - eval_always - desc { "check for overlap between inherent impls defined in this crate" } - } + /// Checks all types in the crate for overlap in their inherent impls. Reports errors. + /// Not meant to be used directly outside of coherence. + /// (Defined only for `LOCAL_CRATE`.) + query crate_inherent_impls_overlap_check(_: CrateNum) + -> () { + eval_always + desc { "check for overlap between inherent impls defined in this crate" } } - Other { - /// Evaluates a constant and returns the computed allocation. - /// - /// **Do not use this** directly, use the `tcx.eval_static_initializer` wrapper. - query eval_to_allocation_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) - -> EvalToAllocationRawResult<'tcx> { - desc { |tcx| - "const-evaluating + checking `{}`", - key.value.display(tcx) - } - cache_on_disk_if { true } + /// Check whether the function has any recursion that could cause the inliner to trigger + /// a cycle. Returns the call stack causing the cycle. The call stack does not contain the + /// current function, just all intermediate functions. + query mir_callgraph_reachable(key: (ty::Instance<'tcx>, LocalDefId)) -> bool { + fatal_cycle + desc { |tcx| + "computing if `{}` (transitively) calls `{}`", + key.0, + tcx.def_path_str(key.1.to_def_id()), } + } - /// Evaluates const items or anonymous constants - /// (such as enum variant explicit discriminants or array lengths) - /// into a representation suitable for the type system and const generics. - /// - /// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`, - /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`. - query eval_to_const_value_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) - -> EvalToConstValueResult<'tcx> { - desc { |tcx| - "simplifying constant for the type system `{}`", - key.value.display(tcx) - } - cache_on_disk_if { true } + /// Obtain all the calls into other local functions + query mir_inliner_callees(key: ty::InstanceDef<'tcx>) -> &'tcx [(DefId, SubstsRef<'tcx>)] { + fatal_cycle + desc { |tcx| + "computing all local function calls in `{}`", + tcx.def_path_str(key.def_id()), } + } - /// Destructure a constant ADT or array into its variant index and its - /// field values. - query destructure_const( - key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>> - ) -> mir::DestructuredConst<'tcx> { - desc { "destructure constant" } + /// Evaluates a constant and returns the computed allocation. + /// + /// **Do not use this** directly, use the `tcx.eval_static_initializer` wrapper. + query eval_to_allocation_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + -> EvalToAllocationRawResult<'tcx> { + desc { |tcx| + "const-evaluating + checking `{}`", + key.value.display(tcx) } + cache_on_disk_if { true } + } - /// Dereference a constant reference or raw pointer and turn the result into a constant - /// again. - query deref_const( - key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>> - ) -> &'tcx ty::Const<'tcx> { - desc { "deref constant" } - } + /// Evaluates const items or anonymous constants + /// (such as enum variant explicit discriminants or array lengths) + /// into a representation suitable for the type system and const generics. + /// + /// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`, + /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`. + query eval_to_const_value_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + -> EvalToConstValueResult<'tcx> { + desc { |tcx| + "simplifying constant for the type system `{}`", + key.value.display(tcx) + } + cache_on_disk_if { true } + } - query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> { - desc { "get a &core::panic::Location referring to a span" } - } + /// Destructure a constant ADT or array into its variant index and its + /// field values. + query destructure_const( + key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>> + ) -> mir::DestructuredConst<'tcx> { + desc { "destructure constant" } + } - query lit_to_const( - key: LitToConstInput<'tcx> - ) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> { - desc { "converting literal to const" } - } + /// Dereference a constant reference or raw pointer and turn the result into a constant + /// again. + query deref_const( + key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>> + ) -> &'tcx ty::Const<'tcx> { + desc { "deref constant" } } - TypeChecking { - query check_match(key: DefId) { - desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } - cache_on_disk_if { key.is_local() } - } + query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> { + desc { "get a &core::panic::Location referring to a span" } + } - /// Performs part of the privacy check and computes "access levels". - query privacy_access_levels(_: CrateNum) -> &'tcx AccessLevels { - eval_always - desc { "privacy access levels" } - } - query check_private_in_public(_: CrateNum) -> () { - eval_always - desc { "checking for private elements in public interfaces" } - } + query lit_to_const( + key: LitToConstInput<'tcx> + ) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> { + desc { "converting literal to const" } } - Other { - query reachable_set(_: CrateNum) -> FxHashSet { - storage(ArenaCacheSelector<'tcx>) - desc { "reachability" } - } + query check_match(key: DefId) { + desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } - /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; - /// in the case of closures, this will be redirected to the enclosing function. - query region_scope_tree(def_id: DefId) -> &'tcx region::ScopeTree { - desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) } - } + /// Performs part of the privacy check and computes "access levels". + query privacy_access_levels(_: CrateNum) -> &'tcx AccessLevels { + eval_always + desc { "privacy access levels" } + } + query check_private_in_public(_: CrateNum) -> () { + eval_always + desc { "checking for private elements in public interfaces" } + } - query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> { - storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } - } + query reachable_set(_: CrateNum) -> FxHashSet { + storage(ArenaCacheSelector<'tcx>) + desc { "reachability" } + } - /// The `symbol_name` query provides the symbol name for calling a - /// given instance from the local crate. In particular, it will also - /// look up the correct symbol name of instances from upstream crates. - query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName<'tcx> { - desc { "computing the symbol for `{}`", key } - cache_on_disk_if { true } - } + /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; + /// in the case of closures, this will be redirected to the enclosing function. + query region_scope_tree(def_id: DefId) -> &'tcx region::ScopeTree { + desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) } + } - query def_kind(def_id: DefId) -> DefKind { - desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } - } - query def_span(def_id: DefId) -> Span { - desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } - // FIXME(mw): DefSpans are not really inputs since they are derived from - // HIR. But at the moment HIR hashing still contains some hacks that allow - // to make type debuginfo to be source location independent. Declaring - // DefSpan an input makes sure that changes to these are always detected - // regardless of HIR hashing. - eval_always - } - query lookup_stability(def_id: DefId) -> Option<&'tcx attr::Stability> { - desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } - } - query lookup_const_stability(def_id: DefId) -> Option<&'tcx attr::ConstStability> { - desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } - } - query lookup_deprecation_entry(def_id: DefId) -> Option { - desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } - } - query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] { - desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } - } + query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } } - Codegen { - query codegen_fn_attrs(def_id: DefId) -> CodegenFnAttrs { - desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } - storage(ArenaCacheSelector<'tcx>) - cache_on_disk_if { true } - } + /// The `symbol_name` query provides the symbol name for calling a + /// given instance from the local crate. In particular, it will also + /// look up the correct symbol name of instances from upstream crates. + query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName<'tcx> { + desc { "computing the symbol for `{}`", key } + cache_on_disk_if { true } } - Other { - query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] { - desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } - } - /// Gets the rendered value of the specified constant or associated constant. - /// Used by rustdoc. - query rendered_const(def_id: DefId) -> String { - desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) } - } - query impl_parent(def_id: DefId) -> Option { - desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } - } + query opt_def_kind(def_id: DefId) -> Option { + desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } } - TypeChecking { - query trait_of_item(def_id: DefId) -> Option { - desc { |tcx| "finding trait defining `{}`", tcx.def_path_str(def_id) } - } + query def_span(def_id: DefId) -> Span { + desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } + // FIXME(mw): DefSpans are not really inputs since they are derived from + // HIR. But at the moment HIR hashing still contains some hacks that allow + // to make type debuginfo to be source location independent. Declaring + // DefSpan an input makes sure that changes to these are always detected + // regardless of HIR hashing. + eval_always } - Codegen { - query is_mir_available(key: DefId) -> bool { - desc { |tcx| "checking if item has mir available: `{}`", tcx.def_path_str(key) } - } + query def_ident_span(def_id: DefId) -> Option { + desc { |tcx| "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) } } - Other { - query vtable_methods(key: ty::PolyTraitRef<'tcx>) - -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { - desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) } - } + query lookup_stability(def_id: DefId) -> Option<&'tcx attr::Stability> { + desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } } - Codegen { - query codegen_fulfill_obligation( - key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) - ) -> Result, ErrorReported> { - cache_on_disk_if { true } - desc { |tcx| - "checking if `{}` fulfills its obligations", - tcx.def_path_str(key.1.def_id()) - } - } + query lookup_const_stability(def_id: DefId) -> Option<&'tcx attr::ConstStability> { + desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } } - TypeChecking { - query all_local_trait_impls(key: CrateNum) -> &'tcx BTreeMap> { - desc { "local trait impls" } - } - query trait_impls_of(key: DefId) -> ty::trait_def::TraitImpls { - storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "trait impls of `{}`", tcx.def_path_str(key) } - } - query specialization_graph_of(key: DefId) -> specialization_graph::Graph { - storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(key) } - cache_on_disk_if { true } - } - query object_safety_violations(key: DefId) -> &'tcx [traits::ObjectSafetyViolation] { - desc { |tcx| "determine object safety of trait `{}`", tcx.def_path_str(key) } - } + query lookup_deprecation_entry(def_id: DefId) -> Option { + desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } + } - /// Gets the ParameterEnvironment for a given item; this environment - /// will be in "user-facing" mode, meaning that it is suitable for - /// type-checking etc, and it does not normalize specializable - /// associated types. This is almost always what you want, - /// unless you are doing MIR optimizations, in which case you - query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> { - desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } - } + query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] { + desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } + } - /// Like `param_env`, but returns the `ParamEnv in `Reveal::All` mode. - /// Prefer this over `tcx.param_env(def_id).with_reveal_all_normalized(tcx)`, - /// as this method is more efficient. - query param_env_reveal_all_normalized(def_id: DefId) -> ty::ParamEnv<'tcx> { - desc { |tcx| "computing revealed normalized predicates of `{}`", tcx.def_path_str(def_id) } - } + query codegen_fn_attrs(def_id: DefId) -> CodegenFnAttrs { + desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { true } + } - /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`, - /// `ty.is_copy()`, etc, since that will prune the environment where possible. - query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `Copy`", env.value } - } - /// Query backing `TyS::is_sized`. - query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is `Sized`", env.value } - } - /// Query backing `TyS::is_freeze`. - query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` is freeze", env.value } - } - /// Query backing `TyS::needs_drop`. - query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { - desc { "computing whether `{}` needs drop", env.value } - } + query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] { + desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } + } + /// Gets the rendered value of the specified constant or associated constant. + /// Used by rustdoc. + query rendered_const(def_id: DefId) -> String { + desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) } + } + query impl_parent(def_id: DefId) -> Option { + desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } + } - /// Query backing `TyS::is_structural_eq_shallow`. - /// - /// This is only correct for ADTs. Call `is_structural_eq_shallow` to handle all types - /// correctly. - query has_structural_eq_impls(ty: Ty<'tcx>) -> bool { - desc { - "computing whether `{:?}` implements `PartialStructuralEq` and `StructuralEq`", - ty - } - } + /// Given an `associated_item`, find the trait it belongs to. + /// Return `None` if the `DefId` is not an associated item. + query trait_of_item(associated_item: DefId) -> Option { + desc { |tcx| "finding trait defining `{}`", tcx.def_path_str(associated_item) } + } - /// A list of types where the ADT requires drop if and only if any of - /// those types require drop. If the ADT is known to always need drop - /// then `Err(AlwaysRequiresDrop)` is returned. - query adt_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { - desc { |tcx| "computing when `{}` needs drop", tcx.def_path_str(def_id) } - cache_on_disk_if { true } - } + query is_ctfe_mir_available(key: DefId) -> bool { + desc { |tcx| "checking if item has ctfe mir available: `{}`", tcx.def_path_str(key) } + } + query is_mir_available(key: DefId) -> bool { + desc { |tcx| "checking if item has mir available: `{}`", tcx.def_path_str(key) } + } - query layout_raw( - env: ty::ParamEnvAnd<'tcx, Ty<'tcx>> - ) -> Result<&'tcx rustc_target::abi::Layout, ty::layout::LayoutError<'tcx>> { - desc { "computing layout of `{}`", env.value } - } + query vtable_methods(key: ty::PolyTraitRef<'tcx>) + -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { + desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) } } - Other { - query dylib_dependency_formats(_: CrateNum) - -> &'tcx [(CrateNum, LinkagePreference)] { - desc { "dylib dependency formats of crate" } + query codegen_fulfill_obligation( + key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) + ) -> Result, ErrorReported> { + cache_on_disk_if { true } + desc { |tcx| + "checking if `{}` fulfills its obligations", + tcx.def_path_str(key.1.def_id()) } + } - query dependency_formats(_: CrateNum) - -> Lrc - { - desc { "get the linkage format of all dependencies" } - } + /// Return all `impl` blocks in the current crate. + /// + /// To allow caching this between crates, you must pass in [`LOCAL_CRATE`] as the crate number. + /// Passing in any other crate will cause an ICE. + /// + /// [`LOCAL_CRATE`]: rustc_hir::def_id::LOCAL_CRATE + query all_local_trait_impls(local_crate: CrateNum) -> &'tcx BTreeMap> { + desc { "local trait impls" } } - Codegen { - query is_compiler_builtins(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate is_compiler_builtins" } - } - query has_global_allocator(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate has_global_allocator" } - } - query has_panic_handler(_: CrateNum) -> bool { - fatal_cycle - desc { "checking if the crate has_panic_handler" } - } - query is_profiler_runtime(_: CrateNum) -> bool { - fatal_cycle - desc { "query a crate is `#![profiler_runtime]`" } - } - query panic_strategy(_: CrateNum) -> PanicStrategy { - fatal_cycle - desc { "query a crate's configured panic strategy" } - } - query is_no_builtins(_: CrateNum) -> bool { - fatal_cycle - desc { "test whether a crate has `#![no_builtins]`" } - } - query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { - fatal_cycle - desc { "query a crate's symbol mangling version" } - } + /// Given a trait `trait_id`, return all known `impl` blocks. + query trait_impls_of(trait_id: DefId) -> ty::trait_def::TraitImpls { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "trait impls of `{}`", tcx.def_path_str(trait_id) } + } - query extern_crate(def_id: DefId) -> Option<&'tcx ExternCrate> { - eval_always - desc { "getting crate's ExternCrateData" } - } + query specialization_graph_of(trait_id: DefId) -> specialization_graph::Graph { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } + cache_on_disk_if { true } + } + query object_safety_violations(trait_id: DefId) -> &'tcx [traits::ObjectSafetyViolation] { + desc { |tcx| "determine object safety of trait `{}`", tcx.def_path_str(trait_id) } } - TypeChecking { - query specializes(_: (DefId, DefId)) -> bool { - desc { "computing whether impls specialize one another" } - } - query in_scope_traits_map(_: LocalDefId) - -> Option<&'tcx FxHashMap>> { - eval_always - desc { "traits in scope at a block" } - } + /// Gets the ParameterEnvironment for a given item; this environment + /// will be in "user-facing" mode, meaning that it is suitable for + /// type-checking etc, and it does not normalize specializable + /// associated types. This is almost always what you want, + /// unless you are doing MIR optimizations, in which case you + /// might want to use `reveal_all()` method to change modes. + query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> { + desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } } - Other { - query module_exports(def_id: LocalDefId) -> Option<&'tcx [Export]> { - desc { |tcx| "looking up items exported by `{}`", tcx.def_path_str(def_id.to_def_id()) } - eval_always - } + /// Like `param_env`, but returns the `ParamEnv in `Reveal::All` mode. + /// Prefer this over `tcx.param_env(def_id).with_reveal_all_normalized(tcx)`, + /// as this method is more efficient. + query param_env_reveal_all_normalized(def_id: DefId) -> ty::ParamEnv<'tcx> { + desc { |tcx| "computing revealed normalized predicates of `{}`", tcx.def_path_str(def_id) } } - TypeChecking { - query impl_defaultness(def_id: DefId) -> hir::Defaultness { - desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } - } + /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`, + /// `ty.is_copy()`, etc, since that will prune the environment where possible. + query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Copy`", env.value } + } + /// Query backing `TyS::is_sized`. + query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Sized`", env.value } + } + /// Query backing `TyS::is_freeze`. + query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is freeze", env.value } + } + /// Query backing `TyS::needs_drop`. + query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` needs drop", env.value } + } - query check_item_well_formed(key: LocalDefId) -> () { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } - } - query check_trait_item_well_formed(key: LocalDefId) -> () { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } - } - query check_impl_item_well_formed(key: LocalDefId) -> () { - desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + /// Query backing `TyS::is_structural_eq_shallow`. + /// + /// This is only correct for ADTs. Call `is_structural_eq_shallow` to handle all types + /// correctly. + query has_structural_eq_impls(ty: Ty<'tcx>) -> bool { + desc { + "computing whether `{:?}` implements `PartialStructuralEq` and `StructuralEq`", + ty } } + /// A list of types where the ADT requires drop if and only if any of + /// those types require drop. If the ADT is known to always need drop + /// then `Err(AlwaysRequiresDrop)` is returned. + query adt_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { + desc { |tcx| "computing when `{}` needs drop", tcx.def_path_str(def_id) } + cache_on_disk_if { true } + } - Linking { - // The `DefId`s of all non-generic functions and statics in the given crate - // that can be reached from outside the crate. - // - // We expect this items to be available for being linked to. - // - // This query can also be called for `LOCAL_CRATE`. In this case it will - // compute which items will be reachable to other crates, taking into account - // the kind of crate that is currently compiled. Crates with only a - // C interface have fewer reachable things. - // - // Does not include external symbols that don't have a corresponding DefId, - // like the compiler-generated `main` function and so on. - query reachable_non_generics(_: CrateNum) - -> DefIdMap { - storage(ArenaCacheSelector<'tcx>) - desc { "looking up the exported symbols of a crate" } - } - query is_reachable_non_generic(def_id: DefId) -> bool { - desc { |tcx| "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } + query layout_raw( + env: ty::ParamEnvAnd<'tcx, Ty<'tcx>> + ) -> Result<&'tcx rustc_target::abi::Layout, ty::layout::LayoutError<'tcx>> { + desc { "computing layout of `{}`", env.value } + } + + query dylib_dependency_formats(_: CrateNum) + -> &'tcx [(CrateNum, LinkagePreference)] { + desc { "dylib dependency formats of crate" } + } + + query dependency_formats(_: CrateNum) + -> Lrc + { + desc { "get the linkage format of all dependencies" } + } + + query is_compiler_builtins(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate is_compiler_builtins" } + } + query has_global_allocator(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate has_global_allocator" } + } + query has_panic_handler(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate has_panic_handler" } + } + query is_profiler_runtime(_: CrateNum) -> bool { + fatal_cycle + desc { "query a crate is `#![profiler_runtime]`" } + } + query panic_strategy(_: CrateNum) -> PanicStrategy { + fatal_cycle + desc { "query a crate's configured panic strategy" } + } + query is_no_builtins(_: CrateNum) -> bool { + fatal_cycle + desc { "test whether a crate has `#![no_builtins]`" } + } + query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { + fatal_cycle + desc { "query a crate's symbol mangling version" } + } + + query extern_crate(def_id: DefId) -> Option<&'tcx ExternCrate> { + eval_always + desc { "getting crate's ExternCrateData" } + } + + query specializes(_: (DefId, DefId)) -> bool { + desc { "computing whether impls specialize one another" } + } + query in_scope_traits_map(_: LocalDefId) + -> Option<&'tcx FxHashMap>> { + eval_always + desc { "traits in scope at a block" } + } + + query module_exports(def_id: LocalDefId) -> Option<&'tcx [Export]> { + desc { |tcx| "looking up items exported by `{}`", tcx.def_path_str(def_id.to_def_id()) } + eval_always + } + + query impl_defaultness(def_id: DefId) -> hir::Defaultness { + desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } + } + + query check_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + } + query check_trait_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + } + query check_impl_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + } + + // The `DefId`s of all non-generic functions and statics in the given crate + // that can be reached from outside the crate. + // + // We expect this items to be available for being linked to. + // + // This query can also be called for `LOCAL_CRATE`. In this case it will + // compute which items will be reachable to other crates, taking into account + // the kind of crate that is currently compiled. Crates with only a + // C interface have fewer reachable things. + // + // Does not include external symbols that don't have a corresponding DefId, + // like the compiler-generated `main` function and so on. + query reachable_non_generics(_: CrateNum) + -> DefIdMap { + storage(ArenaCacheSelector<'tcx>) + desc { "looking up the exported symbols of a crate" } + } + query is_reachable_non_generic(def_id: DefId) -> bool { + desc { |tcx| "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } + } + query is_unreachable_local_definition(def_id: DefId) -> bool { + desc { |tcx| + "checking whether `{}` is reachable from outside the crate", + tcx.def_path_str(def_id), } - query is_unreachable_local_definition(def_id: DefId) -> bool { + } + + /// The entire set of monomorphizations the local crate can safely link + /// to because they are exported from upstream crates. Do not depend on + /// this directly, as its value changes anytime a monomorphization gets + /// added or removed in any upstream crate. Instead use the narrower + /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even + /// better, `Instance::upstream_monomorphization()`. + query upstream_monomorphizations( + k: CrateNum + ) -> DefIdMap, CrateNum>> { + storage(ArenaCacheSelector<'tcx>) + desc { "collecting available upstream monomorphizations `{:?}`", k } + } + + /// Returns the set of upstream monomorphizations available for the + /// generic function identified by the given `def_id`. The query makes + /// sure to make a stable selection if the same monomorphization is + /// available in multiple upstream crates. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + query upstream_monomorphizations_for(def_id: DefId) + -> Option<&'tcx FxHashMap, CrateNum>> { desc { |tcx| - "checking whether `{}` is reachable from outside the crate", + "collecting available upstream monomorphizations for `{}`", tcx.def_path_str(def_id), } } - } - Codegen { - /// The entire set of monomorphizations the local crate can safely link - /// to because they are exported from upstream crates. Do not depend on - /// this directly, as its value changes anytime a monomorphization gets - /// added or removed in any upstream crate. Instead use the narrower - /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even - /// better, `Instance::upstream_monomorphization()`. - query upstream_monomorphizations( - k: CrateNum - ) -> DefIdMap, CrateNum>> { - storage(ArenaCacheSelector<'tcx>) - desc { "collecting available upstream monomorphizations `{:?}`", k } - } + /// Returns the upstream crate that exports drop-glue for the given + /// type (`substs` is expected to be a single-item list containing the + /// type one wants drop-glue for). + /// + /// This is a subset of `upstream_monomorphizations_for` in order to + /// increase dep-tracking granularity. Otherwise adding or removing any + /// type with drop-glue in any upstream crate would invalidate all + /// functions calling drop-glue of an upstream type. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + /// + /// NOTE: This query could easily be extended to also support other + /// common functions that have are large set of monomorphizations + /// (like `Clone::clone` for example). + query upstream_drop_glue_for(substs: SubstsRef<'tcx>) -> Option { + desc { "available upstream drop-glue for `{:?}`", substs } + } - /// Returns the set of upstream monomorphizations available for the - /// generic function identified by the given `def_id`. The query makes - /// sure to make a stable selection if the same monomorphization is - /// available in multiple upstream crates. - /// - /// You likely want to call `Instance::upstream_monomorphization()` - /// instead of invoking this query directly. - query upstream_monomorphizations_for(def_id: DefId) - -> Option<&'tcx FxHashMap, CrateNum>> { - desc { |tcx| - "collecting available upstream monomorphizations for `{}`", - tcx.def_path_str(def_id), - } - } + query foreign_modules(_: CrateNum) -> Lrc> { + desc { "looking up the foreign modules of a linked crate" } + } - /// Returns the upstream crate that exports drop-glue for the given - /// type (`substs` is expected to be a single-item list containing the - /// type one wants drop-glue for). - /// - /// This is a subset of `upstream_monomorphizations_for` in order to - /// increase dep-tracking granularity. Otherwise adding or removing any - /// type with drop-glue in any upstream crate would invalidate all - /// functions calling drop-glue of an upstream type. - /// - /// You likely want to call `Instance::upstream_monomorphization()` - /// instead of invoking this query directly. - /// - /// NOTE: This query could easily be extended to also support other - /// common functions that have are large set of monomorphizations - /// (like `Clone::clone` for example). - query upstream_drop_glue_for(substs: SubstsRef<'tcx>) -> Option { - desc { "available upstream drop-glue for `{:?}`", substs } - } + /// Identifies the entry-point (e.g., the `main` function) for a given + /// crate, returning `None` if there is no entry point (such as for library crates). + query entry_fn(_: CrateNum) -> Option<(LocalDefId, EntryFnType)> { + desc { "looking up the entry function of a crate" } + } + query plugin_registrar_fn(_: CrateNum) -> Option { + desc { "looking up the plugin registrar for a crate" } + } + query proc_macro_decls_static(_: CrateNum) -> Option { + desc { "looking up the derive registrar for a crate" } + } + query crate_disambiguator(_: CrateNum) -> CrateDisambiguator { + eval_always + desc { "looking up the disambiguator a crate" } + } + // The macro which defines `rustc_metadata::provide_extern` depends on this query's name. + // Changing the name should cause a compiler error, but in case that changes, be aware. + query crate_hash(_: CrateNum) -> Svh { + eval_always + desc { "looking up the hash a crate" } + } + query crate_host_hash(_: CrateNum) -> Option { + eval_always + desc { "looking up the hash of a host version of a crate" } + } + query original_crate_name(_: CrateNum) -> Symbol { + eval_always + desc { "looking up the original name a crate" } + } + query extra_filename(_: CrateNum) -> String { + eval_always + desc { "looking up the extra filename for a crate" } + } + query crate_extern_paths(_: CrateNum) -> Vec { + eval_always + desc { "looking up the paths for extern crates" } } - Other { - query foreign_modules(_: CrateNum) -> Lrc> { - desc { "looking up the foreign modules of a linked crate" } - } + /// Given a crate and a trait, look up all impls of that trait in the crate. + /// Return `(impl_id, self_ty)`. + query implementations_of_trait(_: (CrateNum, DefId)) + -> &'tcx [(DefId, Option)] { + desc { "looking up implementations of a trait in a crate" } + } - /// Identifies the entry-point (e.g., the `main` function) for a given - /// crate, returning `None` if there is no entry point (such as for library crates). - query entry_fn(_: CrateNum) -> Option<(LocalDefId, EntryFnType)> { - desc { "looking up the entry function of a crate" } - } - query plugin_registrar_fn(_: CrateNum) -> Option { - desc { "looking up the plugin registrar for a crate" } - } - query proc_macro_decls_static(_: CrateNum) -> Option { - desc { "looking up the derive registrar for a crate" } - } - query crate_disambiguator(_: CrateNum) -> CrateDisambiguator { - eval_always - desc { "looking up the disambiguator a crate" } - } - query crate_hash(_: CrateNum) -> Svh { - eval_always - desc { "looking up the hash a crate" } - } - query crate_host_hash(_: CrateNum) -> Option { - eval_always - desc { "looking up the hash of a host version of a crate" } - } - query original_crate_name(_: CrateNum) -> Symbol { - eval_always - desc { "looking up the original name a crate" } - } - query extra_filename(_: CrateNum) -> String { - eval_always - desc { "looking up the extra filename for a crate" } - } - query crate_extern_paths(_: CrateNum) -> Vec { - eval_always - desc { "looking up the paths for extern crates" } - } + /// Given a crate, look up all trait impls in that crate. + /// Return `(impl_id, self_ty)`. + query all_trait_implementations(_: CrateNum) + -> &'tcx [(DefId, Option)] { + desc { "looking up all (?) trait implementations" } } - TypeChecking { - query implementations_of_trait(_: (CrateNum, DefId)) - -> &'tcx [(DefId, Option)] { - desc { "looking up implementations of a trait in a crate" } - } - query all_trait_implementations(_: CrateNum) - -> &'tcx [(DefId, Option)] { - desc { "looking up all (?) trait implementations" } - } + query is_dllimport_foreign_item(def_id: DefId) -> bool { + desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) } + } + query is_statically_included_foreign_item(def_id: DefId) -> bool { + desc { |tcx| "is_statically_included_foreign_item({})", tcx.def_path_str(def_id) } + } + query native_library_kind(def_id: DefId) + -> Option { + desc { |tcx| "native_library_kind({})", tcx.def_path_str(def_id) } } - Other { - query dllimport_foreign_items(_: CrateNum) - -> FxHashSet { - storage(ArenaCacheSelector<'tcx>) - desc { "dllimport_foreign_items" } - } - query is_dllimport_foreign_item(def_id: DefId) -> bool { - desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) } - } - query is_statically_included_foreign_item(def_id: DefId) -> bool { - desc { |tcx| "is_statically_included_foreign_item({})", tcx.def_path_str(def_id) } - } - query native_library_kind(def_id: DefId) - -> Option { - desc { |tcx| "native_library_kind({})", tcx.def_path_str(def_id) } - } + query link_args(_: CrateNum) -> Lrc> { + eval_always + desc { "looking up link arguments for a crate" } } - Linking { - query link_args(_: CrateNum) -> Lrc> { - eval_always - desc { "looking up link arguments for a crate" } - } + /// Lifetime resolution. See `middle::resolve_lifetimes`. + query resolve_lifetimes(_: CrateNum) -> ResolveLifetimes { + storage(ArenaCacheSelector<'tcx>) + desc { "resolving lifetimes" } + } + query named_region_map(_: LocalDefId) -> + Option<&'tcx FxHashMap> { + desc { "looking up a named region" } + } + query is_late_bound_map(_: LocalDefId) -> + Option<(LocalDefId, &'tcx FxHashSet)> { + desc { "testing if a region is late bound" } + } + query object_lifetime_defaults_map(_: LocalDefId) + -> Option<&'tcx FxHashMap>> { + desc { "looking up lifetime defaults for a region" } } - BorrowChecking { - /// Lifetime resolution. See `middle::resolve_lifetimes`. - query resolve_lifetimes(_: CrateNum) -> ResolveLifetimes { - storage(ArenaCacheSelector<'tcx>) - desc { "resolving lifetimes" } - } - query named_region_map(_: LocalDefId) -> - Option<&'tcx FxHashMap> { - desc { "looking up a named region" } - } - query is_late_bound_map(_: LocalDefId) -> - Option<&'tcx FxHashSet> { - desc { "testing if a region is late bound" } - } - query object_lifetime_defaults_map(_: LocalDefId) - -> Option<&'tcx FxHashMap>> { - desc { "looking up lifetime defaults for a region" } - } + query visibility(def_id: DefId) -> ty::Visibility { + eval_always + desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } } - TypeChecking { - query visibility(def_id: DefId) -> ty::Visibility { - eval_always - desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } - } + /// Computes the set of modules from which this type is visibly uninhabited. + /// To check whether a type is uninhabited at all (not just from a given module), you could + /// check whether the forest is empty. + query type_uninhabited_from( + key: ty::ParamEnvAnd<'tcx, Ty<'tcx>> + ) -> ty::inhabitedness::DefIdForest { + desc { "computing the inhabitedness of `{:?}`", key } } - Other { - query dep_kind(_: CrateNum) -> CrateDepKind { - eval_always - desc { "fetching what a dependency looks like" } - } - query crate_name(_: CrateNum) -> Symbol { - eval_always - desc { "fetching what a crate is named" } - } - query item_children(def_id: DefId) -> &'tcx [Export] { - desc { |tcx| "collecting child items of `{}`", tcx.def_path_str(def_id) } - } - query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option { - desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id.to_def_id()) } - } + query dep_kind(_: CrateNum) -> CrateDepKind { + eval_always + desc { "fetching what a dependency looks like" } + } + query crate_name(_: CrateNum) -> Symbol { + eval_always + desc { "fetching what a crate is named" } + } + query item_children(def_id: DefId) -> &'tcx [Export] { + desc { |tcx| "collecting child items of `{}`", tcx.def_path_str(def_id) } + } + query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option { + desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id.to_def_id()) } + } - query get_lib_features(_: CrateNum) -> LibFeatures { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "calculating the lib features map" } - } - query defined_lib_features(_: CrateNum) - -> &'tcx [(Symbol, Option)] { - desc { "calculating the lib features defined in a crate" } - } - /// Returns the lang items defined in another crate by loading it from metadata. - // FIXME: It is illegal to pass a `CrateNum` other than `LOCAL_CRATE` here, just get rid - // of that argument? - query get_lang_items(_: CrateNum) -> LanguageItems { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "calculating the lang items map" } - } + query get_lib_features(_: CrateNum) -> LibFeatures { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the lib features map" } + } + query defined_lib_features(_: CrateNum) + -> &'tcx [(Symbol, Option)] { + desc { "calculating the lib features defined in a crate" } + } + /// Returns the lang items defined in another crate by loading it from metadata. + // FIXME: It is illegal to pass a `CrateNum` other than `LOCAL_CRATE` here, just get rid + // of that argument? + query get_lang_items(_: CrateNum) -> LanguageItems { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the lang items map" } + } - /// Returns all diagnostic items defined in all crates. - query all_diagnostic_items(_: CrateNum) -> FxHashMap { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "calculating the diagnostic items map" } - } + /// Returns all diagnostic items defined in all crates. + query all_diagnostic_items(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the diagnostic items map" } + } - /// Returns the lang items defined in another crate by loading it from metadata. - query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] { - desc { "calculating the lang items defined in a crate" } - } + /// Returns the lang items defined in another crate by loading it from metadata. + query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] { + desc { "calculating the lang items defined in a crate" } + } - /// Returns the diagnostic items defined in a crate. - query diagnostic_items(_: CrateNum) -> FxHashMap { - storage(ArenaCacheSelector<'tcx>) - desc { "calculating the diagnostic items map in a crate" } - } + /// Returns the diagnostic items defined in a crate. + query diagnostic_items(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the diagnostic items map in a crate" } + } - query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { - desc { "calculating the missing lang items in a crate" } - } - query visible_parent_map(_: CrateNum) - -> DefIdMap { - storage(ArenaCacheSelector<'tcx>) - desc { "calculating the visible parent map" } - } - query trimmed_def_paths(_: CrateNum) - -> FxHashMap { - storage(ArenaCacheSelector<'tcx>) - desc { "calculating trimmed def paths" } - } - query missing_extern_crate_item(_: CrateNum) -> bool { - eval_always - desc { "seeing if we're missing an `extern crate` item for this crate" } - } - query used_crate_source(_: CrateNum) -> Lrc { - eval_always - desc { "looking at the source for a crate" } - } - query postorder_cnums(_: CrateNum) -> &'tcx [CrateNum] { - eval_always - desc { "generating a postorder list of CrateNums" } - } + query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { + desc { "calculating the missing lang items in a crate" } + } + query visible_parent_map(_: CrateNum) + -> DefIdMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the visible parent map" } + } + query trimmed_def_paths(_: CrateNum) + -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating trimmed def paths" } + } + query missing_extern_crate_item(_: CrateNum) -> bool { + eval_always + desc { "seeing if we're missing an `extern crate` item for this crate" } + } + query used_crate_source(_: CrateNum) -> Lrc { + eval_always + desc { "looking at the source for a crate" } + } + query postorder_cnums(_: CrateNum) -> &'tcx [CrateNum] { + eval_always + desc { "generating a postorder list of CrateNums" } + } - query upvars_mentioned(def_id: DefId) -> Option<&'tcx FxIndexMap> { - desc { |tcx| "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) } - eval_always - } - query maybe_unused_trait_import(def_id: LocalDefId) -> bool { - eval_always - desc { |tcx| "maybe_unused_trait_import for `{}`", tcx.def_path_str(def_id.to_def_id()) } - } - query maybe_unused_extern_crates(_: CrateNum) - -> &'tcx [(LocalDefId, Span)] { - eval_always - desc { "looking up all possibly unused extern crates" } - } - query names_imported_by_glob_use(def_id: LocalDefId) - -> &'tcx FxHashSet { - eval_always - desc { |tcx| "names_imported_by_glob_use for `{}`", tcx.def_path_str(def_id.to_def_id()) } - } + query upvars_mentioned(def_id: DefId) -> Option<&'tcx FxIndexMap> { + desc { |tcx| "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) } + eval_always + } + query maybe_unused_trait_import(def_id: LocalDefId) -> bool { + eval_always + desc { |tcx| "maybe_unused_trait_import for `{}`", tcx.def_path_str(def_id.to_def_id()) } + } + query maybe_unused_extern_crates(_: CrateNum) + -> &'tcx [(LocalDefId, Span)] { + eval_always + desc { "looking up all possibly unused extern crates" } + } + query names_imported_by_glob_use(def_id: LocalDefId) + -> &'tcx FxHashSet { + eval_always + desc { |tcx| "names_imported_by_glob_use for `{}`", tcx.def_path_str(def_id.to_def_id()) } + } - query stability_index(_: CrateNum) -> stability::Index<'tcx> { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "calculating the stability index for the local crate" } - } - query all_crate_nums(_: CrateNum) -> &'tcx [CrateNum] { - eval_always - desc { "fetching all foreign CrateNum instances" } - } + query stability_index(_: CrateNum) -> stability::Index<'tcx> { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the stability index for the local crate" } + } + query all_crate_nums(_: CrateNum) -> &'tcx [CrateNum] { + eval_always + desc { "fetching all foreign CrateNum instances" } + } - /// A vector of every trait accessible in the whole crate - /// (i.e., including those from subcrates). This is used only for - /// error reporting. - query all_traits(_: CrateNum) -> &'tcx [DefId] { - desc { "fetching all foreign and local traits" } - } + /// A vector of every trait accessible in the whole crate + /// (i.e., including those from subcrates). This is used only for + /// error reporting. + query all_traits(_: CrateNum) -> &'tcx [DefId] { + desc { "fetching all foreign and local traits" } } - Linking { - /// The list of symbols exported from the given crate. - /// - /// - All names contained in `exported_symbols(cnum)` are guaranteed to - /// correspond to a publicly visible symbol in `cnum` machine code. - /// - The `exported_symbols` sets of different crates do not intersect. - query exported_symbols(_: CrateNum) - -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] { - desc { "exported_symbols" } - } + /// The list of symbols exported from the given crate. + /// + /// - All names contained in `exported_symbols(cnum)` are guaranteed to + /// correspond to a publicly visible symbol in `cnum` machine code. + /// - The `exported_symbols` sets of different crates do not intersect. + query exported_symbols(_: CrateNum) + -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] { + desc { "exported_symbols" } } - Codegen { - query collect_and_partition_mono_items(_: CrateNum) - -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { - eval_always - desc { "collect_and_partition_mono_items" } - } - query is_codegened_item(def_id: DefId) -> bool { - desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } - } - query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> { - desc { "codegen_unit" } - } - query unused_generic_params(key: DefId) -> FiniteBitSet { - cache_on_disk_if { key.is_local() } - desc { - |tcx| "determining which generic parameters are unused by `{}`", - tcx.def_path_str(key) - } - } - query backend_optimization_level(_: CrateNum) -> OptLevel { - desc { "optimization level used by backend" } + query collect_and_partition_mono_items(_: CrateNum) + -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { + eval_always + desc { "collect_and_partition_mono_items" } + } + query is_codegened_item(def_id: DefId) -> bool { + desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } + } + query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> { + desc { "codegen_unit" } + } + query unused_generic_params(key: DefId) -> FiniteBitSet { + cache_on_disk_if { key.is_local() } + desc { + |tcx| "determining which generic parameters are unused by `{}`", + tcx.def_path_str(key) } } + query backend_optimization_level(_: CrateNum) -> OptLevel { + desc { "optimization level used by backend" } + } - Other { - query output_filenames(_: CrateNum) -> Arc { - eval_always - desc { "output_filenames" } - } + query output_filenames(_: CrateNum) -> Arc { + eval_always + desc { "output_filenames" } } - TypeChecking { - /// Do not call this query directly: invoke `normalize` instead. - query normalize_projection_ty( - goal: CanonicalProjectionGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } + /// Do not call this query directly: invoke `normalize` instead. + query normalize_projection_ty( + goal: CanonicalProjectionGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } - /// Do not call this query directly: invoke `normalize_erasing_regions` instead. - query normalize_generic_arg_after_erasing_regions( - goal: ParamEnvAnd<'tcx, GenericArg<'tcx>> - ) -> GenericArg<'tcx> { - desc { "normalizing `{}`", goal.value } - } + /// Do not call this query directly: invoke `normalize_erasing_regions` instead. + query normalize_generic_arg_after_erasing_regions( + goal: ParamEnvAnd<'tcx, GenericArg<'tcx>> + ) -> GenericArg<'tcx> { + desc { "normalizing `{}`", goal.value } + } - query implied_outlives_bounds( - goal: CanonicalTyGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, - NoSolution, - > { - desc { "computing implied outlives bounds for `{:?}`", goal } - } + query implied_outlives_bounds( + goal: CanonicalTyGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec>>>, + NoSolution, + > { + desc { "computing implied outlives bounds for `{:?}`", goal } + } - /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead. - query dropck_outlives( - goal: CanonicalTyGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, - NoSolution, - > { - desc { "computing dropck types for `{:?}`", goal } - } + /// Do not call this query directly: invoke `infcx.at().dropck_outlives()` instead. + query dropck_outlives( + goal: CanonicalTyGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, + NoSolution, + > { + desc { "computing dropck types for `{:?}`", goal } + } - /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or - /// `infcx.predicate_must_hold()` instead. - query evaluate_obligation( - goal: CanonicalPredicateGoal<'tcx> - ) -> Result { - desc { "evaluating trait selection obligation `{}`", goal.value.value } - } + /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or + /// `infcx.predicate_must_hold()` instead. + query evaluate_obligation( + goal: CanonicalPredicateGoal<'tcx> + ) -> Result { + desc { "evaluating trait selection obligation `{}`", goal.value.value } + } - query evaluate_goal( - goal: traits::CanonicalChalkEnvironmentAndGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution - > { - desc { "evaluating trait selection obligation `{}`", goal.value } - } + query evaluate_goal( + goal: traits::CanonicalChalkEnvironmentAndGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution + > { + desc { "evaluating trait selection obligation `{}`", goal.value } + } - query type_implements_trait( - key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, ) - ) -> bool { - desc { "evaluating `type_implements_trait` `{:?}`", key } - } + query type_implements_trait( + key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, ) + ) -> bool { + desc { "evaluating `type_implements_trait` `{:?}`", key } + } - /// Do not call this query directly: part of the `Eq` type-op - query type_op_ascribe_user_type( - goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal } - } + /// Do not call this query directly: part of the `Eq` type-op + query type_op_ascribe_user_type( + goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal } + } - /// Do not call this query directly: part of the `Eq` type-op - query type_op_eq( - goal: CanonicalTypeOpEqGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_eq` `{:?}`", goal } - } + /// Do not call this query directly: part of the `Eq` type-op + query type_op_eq( + goal: CanonicalTypeOpEqGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_eq` `{:?}`", goal } + } - /// Do not call this query directly: part of the `Subtype` type-op - query type_op_subtype( - goal: CanonicalTypeOpSubtypeGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_subtype` `{:?}`", goal } - } + /// Do not call this query directly: part of the `Subtype` type-op + query type_op_subtype( + goal: CanonicalTypeOpSubtypeGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_subtype` `{:?}`", goal } + } - /// Do not call this query directly: part of the `ProvePredicate` type-op - query type_op_prove_predicate( - goal: CanonicalTypeOpProvePredicateGoal<'tcx> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, - NoSolution, - > { - desc { "evaluating `type_op_prove_predicate` `{:?}`", goal } - } + /// Do not call this query directly: part of the `ProvePredicate` type-op + query type_op_prove_predicate( + goal: CanonicalTypeOpProvePredicateGoal<'tcx> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, + NoSolution, + > { + desc { "evaluating `type_op_prove_predicate` `{:?}`", goal } + } - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_ty( - goal: CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_ty( + goal: CanonicalTypeOpNormalizeGoal<'tcx, Ty<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_predicate( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Predicate<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Predicate<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_predicate( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Predicate<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Predicate<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_poly_fn_sig( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::PolyFnSig<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_poly_fn_sig( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::PolyFnSig<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } - /// Do not call this query directly: part of the `Normalize` type-op - query type_op_normalize_fn_sig( - goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::FnSig<'tcx>> - ) -> Result< - &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>, - NoSolution, - > { - desc { "normalizing `{:?}`", goal } - } + /// Do not call this query directly: part of the `Normalize` type-op + query type_op_normalize_fn_sig( + goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::FnSig<'tcx>> + ) -> Result< + &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>, + NoSolution, + > { + desc { "normalizing `{:?}`", goal } + } - query subst_and_check_impossible_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { - desc { |tcx| - "impossible substituted predicates:`{}`", - tcx.def_path_str(key.0) - } + query subst_and_check_impossible_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool { + desc { |tcx| + "impossible substituted predicates:`{}`", + tcx.def_path_str(key.0) } + } - query method_autoderef_steps( - goal: CanonicalTyGoal<'tcx> - ) -> MethodAutoderefStepsResult<'tcx> { - desc { "computing autoderef types for `{:?}`", goal } - } + query method_autoderef_steps( + goal: CanonicalTyGoal<'tcx> + ) -> MethodAutoderefStepsResult<'tcx> { + desc { "computing autoderef types for `{:?}`", goal } } - Other { - query supported_target_features(_: CrateNum) -> FxHashMap> { - storage(ArenaCacheSelector<'tcx>) - eval_always - desc { "looking up supported target features" } - } + query supported_target_features(_: CrateNum) -> FxHashMap> { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "looking up supported target features" } + } - /// Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. - query instance_def_size_estimate(def: ty::InstanceDef<'tcx>) - -> usize { - desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) } - } + /// Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. + query instance_def_size_estimate(def: ty::InstanceDef<'tcx>) + -> usize { + desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) } + } - query features_query(_: CrateNum) -> &'tcx rustc_feature::Features { - eval_always - desc { "looking up enabled feature gates" } - } + query features_query(_: CrateNum) -> &'tcx rustc_feature::Features { + eval_always + desc { "looking up enabled feature gates" } + } - /// Attempt to resolve the given `DefId` to an `Instance`, for the - /// given generics args (`SubstsRef`), returning one of: - /// * `Ok(Some(instance))` on success - /// * `Ok(None)` when the `SubstsRef` are still too generic, - /// and therefore don't allow finding the final `Instance` - /// * `Err(ErrorReported)` when the `Instance` resolution process - /// couldn't complete due to errors elsewhere - this is distinct - /// from `Ok(None)` to avoid misleading diagnostics when an error - /// has already been/will be emitted, for the original cause - query resolve_instance( - key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)> - ) -> Result>, ErrorReported> { - desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) } - } + /// Attempt to resolve the given `DefId` to an `Instance`, for the + /// given generics args (`SubstsRef`), returning one of: + /// * `Ok(Some(instance))` on success + /// * `Ok(None)` when the `SubstsRef` are still too generic, + /// and therefore don't allow finding the final `Instance` + /// * `Err(ErrorReported)` when the `Instance` resolution process + /// couldn't complete due to errors elsewhere - this is distinct + /// from `Ok(None)` to avoid misleading diagnostics when an error + /// has already been/will be emitted, for the original cause + query resolve_instance( + key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)> + ) -> Result>, ErrorReported> { + desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) } + } - query resolve_instance_of_const_arg( - key: ty::ParamEnvAnd<'tcx, (LocalDefId, DefId, SubstsRef<'tcx>)> - ) -> Result>, ErrorReported> { - desc { - "resolving instance of the const argument `{}`", - ty::Instance::new(key.value.0.to_def_id(), key.value.2), - } + query resolve_instance_of_const_arg( + key: ty::ParamEnvAnd<'tcx, (LocalDefId, DefId, SubstsRef<'tcx>)> + ) -> Result>, ErrorReported> { + desc { + "resolving instance of the const argument `{}`", + ty::Instance::new(key.value.0.to_def_id(), key.value.2), } + } - query normalize_opaque_types(key: &'tcx ty::List>) -> &'tcx ty::List> { - desc { "normalizing opaque types in {:?}", key } - } + query normalize_opaque_types(key: &'tcx ty::List>) -> &'tcx ty::List> { + desc { "normalizing opaque types in {:?}", key } } } diff --git a/compiler/rustc_middle/src/traits/chalk.rs b/compiler/rustc_middle/src/traits/chalk.rs index f864ad8ebc..74873778f7 100644 --- a/compiler/rustc_middle/src/traits/chalk.rs +++ b/compiler/rustc_middle/src/traits/chalk.rs @@ -72,6 +72,7 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { type InternedQuantifiedWhereClauses = Vec>; type InternedVariableKinds = Vec>; type InternedCanonicalVarKinds = Vec>; + type InternedVariances = Vec; type InternedConstraints = Vec>>; type DefId = DefId; type InternedAdtId = &'tcx AdtDef; @@ -86,17 +87,34 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { write!(fmt, "{:?}", pci.consequence)?; let conditions = pci.conditions.interned(); + let constraints = pci.constraints.interned(); let conds = conditions.len(); - if conds == 0 { + let consts = constraints.len(); + if conds == 0 && consts == 0 { return Ok(()); } write!(fmt, " :- ")?; - for cond in &conditions[..conds - 1] { - write!(fmt, "{:?}, ", cond)?; + + if conds != 0 { + for cond in &conditions[..conds - 1] { + write!(fmt, "{:?}, ", cond)?; + } + write!(fmt, "{:?}", conditions[conds - 1])?; + } + + if conds != 0 && consts != 0 { + write!(fmt, " ; ")?; } - write!(fmt, "{:?}", conditions[conds - 1])?; + + if consts != 0 { + for constraint in &constraints[..consts - 1] { + write!(fmt, "{:?}, ", constraint)?; + } + write!(fmt, "{:?}", constraints[consts - 1])?; + } + Ok(()) }; Some(write()) @@ -351,6 +369,20 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { ) -> &'a [chalk_ir::InEnvironment>] { constraints } + + fn intern_variances( + &self, + data: impl IntoIterator>, + ) -> Result { + data.into_iter().collect::, _>>() + } + + fn variances_data<'a>( + &self, + variances: &'a Self::InternedVariances, + ) -> &'a [chalk_ir::Variance] { + variances + } } impl<'tcx> chalk_ir::interner::HasInterner for RustInterner<'tcx> { diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 0a663f793a..163b400973 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -228,8 +228,7 @@ pub enum ObligationCauseCode<'tcx> { /// Inline asm operand type must be `Sized`. InlineAsmSized, /// `[T, ..n]` implies that `T` must be `Copy`. - /// If `true`, suggest `const_in_array_repeat_expressions` feature flag. - RepeatVec(bool), + RepeatVec, /// Types of fields (other than the last, except for packed structs) in a struct must be sized. FieldSized { diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index ec6010e6ee..cb60bfa4c5 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -23,7 +23,7 @@ use rustc_span::symbol::Ident; /// parents of a given specializing impl, which is needed for extracting /// default items amongst other things. In the simple "chain" rule, every impl /// has at most one parent. -#[derive(TyEncodable, TyDecodable, HashStable)] +#[derive(TyEncodable, TyDecodable, HashStable, Debug)] pub struct Graph { /// All impls have a parent; the "root" impls have as their parent the `def_id` /// of the trait. @@ -50,7 +50,7 @@ impl Graph { /// Children of a given impl, grouped into blanket/non-blanket varieties as is /// done in `TraitDef`. -#[derive(Default, TyEncodable, TyDecodable)] +#[derive(Default, TyEncodable, TyDecodable, Debug)] pub struct Children { // Impls of a trait (or specializations of a given impl). To allow for // quicker lookup, the impls are indexed by a simplified version of their diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index b47d9c50e1..20a6af5f6c 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -3,13 +3,12 @@ use crate::ty::{self, Ty}; -use rustc_ast as ast; use rustc_macros::HashStable; /// Types that are represented as ints. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum IntTy { - U(ast::UintTy), + U(ty::UintTy), I, CEnum, Bool, @@ -22,15 +21,16 @@ pub enum CastTy<'tcx> { /// Various types that are represented as ints and handled mostly /// in the same way, merged for easier matching. Int(IntTy), - /// Floating-Point types + /// Floating-point types. Float, - /// Function Pointers + /// Function pointers. FnPtr, - /// Raw pointers + /// Raw pointers. Ptr(ty::TypeAndMut<'tcx>), } -/// Cast Kind. See RFC 401 (or librustc_typeck/check/cast.rs) +/// Cast Kind. See [RFC 401](https://rust-lang.github.io/rfcs/0401-coercions.html) +/// (or librustc_typeck/check/cast.rs). #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub enum CastKind { CoercionCast, @@ -48,7 +48,7 @@ pub enum CastKind { impl<'tcx> CastTy<'tcx> { /// Returns `Some` for integral/pointer casts. - /// casts like unsizing casts will return `None` + /// Casts like unsizing casts will return `None`. pub fn from_ty(t: Ty<'tcx>) -> Option> { match *t.kind() { ty::Bool => Some(CastTy::Int(IntTy::Bool)), diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index df59469021..0dad5df485 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -18,7 +18,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::Span; -use std::convert::{TryFrom, TryInto}; use std::hash::Hash; use std::intrinsics; use std::marker::DiscriminantKind; @@ -43,27 +42,13 @@ impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for Ty<'tcx> { } } -impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for ty::Predicate<'tcx> { +impl<'tcx, E: TyEncoder<'tcx>> EncodableWithShorthand<'tcx, E> for ty::PredicateKind<'tcx> { type Variant = ty::PredicateKind<'tcx>; - fn variant(&self) -> &Self::Variant { - self.kind() - } -} -pub trait OpaqueEncoder: Encoder { - fn opaque(&mut self) -> &mut rustc_serialize::opaque::Encoder; - fn encoder_position(&self) -> usize; -} - -impl OpaqueEncoder for rustc_serialize::opaque::Encoder { #[inline] - fn opaque(&mut self) -> &mut rustc_serialize::opaque::Encoder { + fn variant(&self) -> &Self::Variant { self } - #[inline] - fn encoder_position(&self) -> usize { - self.position() - } } pub trait TyEncoder<'tcx>: Encoder { @@ -71,7 +56,7 @@ pub trait TyEncoder<'tcx>: Encoder { fn position(&self) -> usize; fn type_shorthands(&mut self) -> &mut FxHashMap, usize>; - fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize>; + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize>; fn encode_alloc_id(&mut self, alloc_id: &AllocId) -> Result<(), Self::Error>; } @@ -95,7 +80,8 @@ where E: TyEncoder<'tcx>, M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, T: EncodableWithShorthand<'tcx, E>, - ::Discriminant: Ord + TryFrom, + // The discriminant and shorthand must have the same size. + T::Variant: DiscriminantKind, { let existing_shorthand = cache(encoder).get(value).copied(); if let Some(shorthand) = existing_shorthand { @@ -111,7 +97,7 @@ where // The shorthand encoding uses the same usize as the // discriminant, with an offset so they can't conflict. let discriminant = intrinsics::discriminant_value(variant); - assert!(discriminant < SHORTHAND_OFFSET.try_into().ok().unwrap()); + assert!(SHORTHAND_OFFSET > discriminant as usize); let shorthand = start + SHORTHAND_OFFSET; @@ -134,9 +120,15 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable for Ty<'tcx> { } } +impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Binder> { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + encode_with_shorthand(e, &self.skip_binder(), TyEncoder::predicate_shorthands) + } +} + impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Predicate<'tcx> { fn encode(&self, e: &mut E) -> Result<(), E::Error> { - encode_with_shorthand(e, self, TyEncoder::predicate_shorthands) + self.kind().encode(e) } } @@ -234,18 +226,24 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for Ty<'tcx> { } } -impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Predicate<'tcx> { - fn decode(decoder: &mut D) -> Result, D::Error> { +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Binder> { + fn decode(decoder: &mut D) -> Result>, D::Error> { // Handle shorthands first, if we have an usize > 0x80. - let predicate_kind = if decoder.positioned_at_shorthand() { + Ok(ty::Binder::bind(if decoder.positioned_at_shorthand() { let pos = decoder.read_usize()?; assert!(pos >= SHORTHAND_OFFSET); let shorthand = pos - SHORTHAND_OFFSET; - decoder.with_position(shorthand, ty::PredicateKind::decode) + decoder.with_position(shorthand, ty::PredicateKind::decode)? } else { - ty::PredicateKind::decode(decoder) - }?; + ty::PredicateKind::decode(decoder)? + })) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Predicate<'tcx> { + fn decode(decoder: &mut D) -> Result, D::Error> { + let predicate_kind = Decodable::decode(decoder)?; let predicate = decoder.tcx().mk_predicate(predicate_kind); Ok(predicate) } @@ -473,3 +471,28 @@ macro_rules! implement_ty_decoder { } } } + +macro_rules! impl_binder_encode_decode { + ($($t:ty),+ $(,)?) => { + $( + impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Binder<$t> { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + self.as_ref().skip_binder().encode(e) + } + } + impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Binder<$t> { + fn decode(decoder: &mut D) -> Result { + Ok(ty::Binder::bind(Decodable::decode(decoder)?)) + } + } + )* + } +} + +impl_binder_encode_decode! { + &'tcx ty::List>, + ty::FnSig<'tcx>, + ty::ExistentialPredicate<'tcx>, + ty::TraitRef<'tcx>, + Vec>, +} diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 0af884a286..041c040f0b 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -92,8 +92,7 @@ impl<'tcx> Const<'tcx> { let item_id = tcx.hir().get_parent_node(hir_id); let item_def_id = tcx.hir().local_def_id(item_id); let generics = tcx.generics_of(item_def_id.to_def_id()); - let index = - generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id).to_def_id()]; + let index = generics.param_def_id_to_index[&def_id]; let name = tcx.hir().name(hir_id); ty::ConstKind::Param(ty::ParamConst::new(index, name)) } diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index ecf2837b3e..a2638d8bdd 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -82,7 +82,7 @@ impl<'tcx> ConstKind<'tcx> { /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the /// unevaluated constant. pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { - self.try_eval(tcx, param_env).and_then(Result::ok).map(ConstKind::Value).unwrap_or(self) + self.try_eval(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value) } #[inline] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9b944f202a..1255302f74 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1,8 +1,9 @@ //! Type context book-keeping. use crate::arena::Arena; -use crate::dep_graph::{self, DepGraph, DepKind, DepNode, DepNodeExt}; +use crate::dep_graph::DepGraph; use crate::hir::exports::ExportMap; +use crate::hir::place::Place as HirPlace; use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; @@ -17,11 +18,11 @@ use crate::ty::query::{self, TyCtxtAt}; use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSubsts}; use crate::ty::TyKind::*; use crate::ty::{ - self, AdtDef, AdtKind, BindingMode, BoundVar, CanonicalPolyFnSig, Const, ConstVid, DefIdTree, - ExistentialPredicate, FloatVar, FloatVid, GenericParamDefKind, InferConst, InferTy, IntVar, - IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate, PredicateInner, PredicateKind, - ProjectionTy, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, - TyVid, TypeAndMut, Visibility, + self, AdtDef, AdtKind, Binder, BindingMode, BoundVar, CanonicalPolyFnSig, Const, ConstVid, + DefIdTree, ExistentialPredicate, FloatTy, FloatVar, FloatVid, GenericParamDefKind, InferConst, + InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate, + PredicateInner, PredicateKind, ProjectionTy, Region, RegionKind, ReprOptions, + TraitObjectVisitor, Ty, TyKind, TyS, TyVar, TyVid, TypeAndMut, UintTy, Visibility, }; use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; @@ -37,8 +38,7 @@ use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId}; -use rustc_hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; @@ -47,6 +47,7 @@ use rustc_hir::{ }; use rustc_index::vec::{Idx, IndexVec}; use rustc_macros::HashStable; +use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames}; use rustc_session::lint::{Level, Lint}; use rustc_session::Session; @@ -133,7 +134,7 @@ impl<'tcx> CtxtInterners<'tcx> { } #[inline(never)] - fn intern_predicate(&self, kind: PredicateKind<'tcx>) -> &'tcx PredicateInner<'tcx> { + fn intern_predicate(&self, kind: Binder>) -> &'tcx PredicateInner<'tcx> { self.predicate .intern(kind, |kind| { let flags = super::flags::FlagComputation::for_predicate(kind); @@ -379,7 +380,7 @@ pub struct TypeckResults<'tcx> { /// Records the reasons that we picked the kind of each closure; /// not all closures are present in the map. - closure_kind_origins: ItemLocalMap<(Span, Symbol)>, + closure_kind_origins: ItemLocalMap<(Span, HirPlace<'tcx>)>, /// For each fn, records the "liberated" types of its arguments /// and return type. Liberated means that all bound regions @@ -642,11 +643,13 @@ impl<'tcx> TypeckResults<'tcx> { self.upvar_capture_map[&upvar_id] } - pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, Symbol)> { + pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, HirPlace<'tcx>)> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.closure_kind_origins } } - pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<'_, (Span, Symbol)> { + pub fn closure_kind_origins_mut( + &mut self, + ) -> LocalTableInContextMut<'_, (Span, HirPlace<'tcx>)> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.closure_kind_origins } } @@ -836,20 +839,20 @@ impl<'tcx> CommonTypes<'tcx> { bool: mk(Bool), char: mk(Char), never: mk(Never), - isize: mk(Int(ast::IntTy::Isize)), - i8: mk(Int(ast::IntTy::I8)), - i16: mk(Int(ast::IntTy::I16)), - i32: mk(Int(ast::IntTy::I32)), - i64: mk(Int(ast::IntTy::I64)), - i128: mk(Int(ast::IntTy::I128)), - usize: mk(Uint(ast::UintTy::Usize)), - u8: mk(Uint(ast::UintTy::U8)), - u16: mk(Uint(ast::UintTy::U16)), - u32: mk(Uint(ast::UintTy::U32)), - u64: mk(Uint(ast::UintTy::U64)), - u128: mk(Uint(ast::UintTy::U128)), - f32: mk(Float(ast::FloatTy::F32)), - f64: mk(Float(ast::FloatTy::F64)), + isize: mk(Int(ty::IntTy::Isize)), + i8: mk(Int(ty::IntTy::I8)), + i16: mk(Int(ty::IntTy::I16)), + i32: mk(Int(ty::IntTy::I32)), + i64: mk(Int(ty::IntTy::I64)), + i128: mk(Int(ty::IntTy::I128)), + usize: mk(Uint(ty::UintTy::Usize)), + u8: mk(Uint(ty::UintTy::U8)), + u16: mk(Uint(ty::UintTy::U16)), + u32: mk(Uint(ty::UintTy::U32)), + u64: mk(Uint(ty::UintTy::U64)), + u128: mk(Uint(ty::UintTy::U128)), + f32: mk(Float(ty::FloatTy::F32)), + f64: mk(Float(ty::FloatTy::F64)), str_: mk(Str), self_param: mk(ty::Param(ty::ParamTy { index: 0, name: kw::SelfUpper })), @@ -1314,33 +1317,8 @@ impl<'tcx> TyCtxt<'tcx> { StableHashingContext::ignore_spans(self.sess, krate, self.definitions, &*self.cstore) } - // This method makes sure that we have a DepNode and a Fingerprint for - // every upstream crate. It needs to be called once right after the tcx is - // created. - // With full-fledged red/green, the method will probably become unnecessary - // as this will be done on-demand. - pub fn allocate_metadata_dep_nodes(self) { - // We cannot use the query versions of crates() and crate_hash(), since - // those would need the DepNodes that we are allocating here. - for cnum in self.cstore.crates_untracked() { - let def_path_hash = self.def_path_hash(DefId { krate: cnum, index: CRATE_DEF_INDEX }); - let dep_node = DepNode::from_def_path_hash(def_path_hash, DepKind::CrateMetadata); - let crate_hash = self.cstore.crate_hash_untracked(cnum); - self.dep_graph.with_task( - dep_node, - self, - crate_hash, - |_, x| x, // No transformation needed - dep_graph::hash_result, - ); - } - } - - pub fn serialize_query_result_cache(self, encoder: &mut E) -> Result<(), E::Error> - where - E: ty::codec::OpaqueEncoder, - { - self.queries.on_disk_cache.as_ref().map(|c| c.serialize(self, encoder)).unwrap_or(Ok(())) + pub fn serialize_query_result_cache(self, encoder: &mut FileEncoder) -> FileEncodeResult { + self.queries.on_disk_cache.as_ref().map_or(Ok(()), |c| c.serialize(self, encoder)) } /// If `true`, we should use the MIR-based borrowck, but also @@ -1386,7 +1364,7 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn lazy_normalization(self) -> bool { let features = self.features(); - // Note: We do not enable lazy normalization for `features.min_const_generics`. + // Note: We do not enable lazy normalization for `min_const_generics`. features.const_generics || features.lazy_normalization_consts } @@ -1973,8 +1951,8 @@ impl<'tcx> Hash for Interned<'tcx, PredicateInner<'tcx>> { } } -impl<'tcx> Borrow> for Interned<'tcx, PredicateInner<'tcx>> { - fn borrow<'a>(&'a self) -> &'a PredicateKind<'tcx> { +impl<'tcx> Borrow>> for Interned<'tcx, PredicateInner<'tcx>> { + fn borrow<'a>(&'a self) -> &'a Binder> { &self.0.kind } } @@ -2012,12 +1990,6 @@ impl<'tcx> Borrow> for Interned<'tcx, Const<'tcx>> { } } -impl<'tcx> Borrow> for Interned<'tcx, PredicateKind<'tcx>> { - fn borrow<'a>(&'a self) -> &'a PredicateKind<'tcx> { - &self.0 - } -} - macro_rules! direct_interners { ($($name:ident: $method:ident($ty:ty),)+) => { $(impl<'tcx> PartialEq for Interned<'tcx, $ty> { @@ -2116,8 +2088,8 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_predicate(self, kind: PredicateKind<'tcx>) -> Predicate<'tcx> { - let inner = self.interners.intern_predicate(kind); + pub fn mk_predicate(self, binder: Binder>) -> Predicate<'tcx> { + let inner = self.interners.intern_predicate(binder); Predicate { inner } } @@ -2125,37 +2097,37 @@ impl<'tcx> TyCtxt<'tcx> { pub fn reuse_or_mk_predicate( self, pred: Predicate<'tcx>, - kind: PredicateKind<'tcx>, + binder: Binder>, ) -> Predicate<'tcx> { - if *pred.kind() != kind { self.mk_predicate(kind) } else { pred } + if pred.kind() != binder { self.mk_predicate(binder) } else { pred } } - pub fn mk_mach_int(self, tm: ast::IntTy) -> Ty<'tcx> { + pub fn mk_mach_int(self, tm: IntTy) -> Ty<'tcx> { match tm { - ast::IntTy::Isize => self.types.isize, - ast::IntTy::I8 => self.types.i8, - ast::IntTy::I16 => self.types.i16, - ast::IntTy::I32 => self.types.i32, - ast::IntTy::I64 => self.types.i64, - ast::IntTy::I128 => self.types.i128, + IntTy::Isize => self.types.isize, + IntTy::I8 => self.types.i8, + IntTy::I16 => self.types.i16, + IntTy::I32 => self.types.i32, + IntTy::I64 => self.types.i64, + IntTy::I128 => self.types.i128, } } - pub fn mk_mach_uint(self, tm: ast::UintTy) -> Ty<'tcx> { + pub fn mk_mach_uint(self, tm: UintTy) -> Ty<'tcx> { match tm { - ast::UintTy::Usize => self.types.usize, - ast::UintTy::U8 => self.types.u8, - ast::UintTy::U16 => self.types.u16, - ast::UintTy::U32 => self.types.u32, - ast::UintTy::U64 => self.types.u64, - ast::UintTy::U128 => self.types.u128, + UintTy::Usize => self.types.usize, + UintTy::U8 => self.types.u8, + UintTy::U16 => self.types.u16, + UintTy::U32 => self.types.u32, + UintTy::U64 => self.types.u64, + UintTy::U128 => self.types.u128, } } - pub fn mk_mach_float(self, tm: ast::FloatTy) -> Ty<'tcx> { + pub fn mk_mach_float(self, tm: FloatTy) -> Ty<'tcx> { match tm { - ast::FloatTy::F32 => self.types.f32, - ast::FloatTy::F64 => self.types.f64, + FloatTy::F32 => self.types.f32, + FloatTy::F64 => self.types.f64, } } @@ -2603,7 +2575,8 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn is_late_bound(self, id: HirId) -> bool { - self.is_late_bound_map(id.owner).map(|set| set.contains(&id.local_id)).unwrap_or(false) + self.is_late_bound_map(id.owner) + .map_or(false, |(owner, set)| owner == id.owner && set.contains(&id.local_id)) } pub fn object_lifetime_defaults(self, id: HirId) -> Option<&'tcx [ObjectLifetimeDefault]> { diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 65703d04c7..4a131a4ec0 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -1,8 +1,7 @@ //! Diagnostics related methods for `TyS`. -use crate::ty::sty::InferTy; use crate::ty::TyKind::*; -use crate::ty::{TyCtxt, TyS}; +use crate::ty::{InferTy, TyCtxt, TyS}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -13,13 +12,17 @@ impl<'tcx> TyS<'tcx> { pub fn is_primitive_ty(&self) -> bool { matches!( self.kind(), - Bool | Char | Str | Int(_) | Uint(_) | Float(_) - | Infer( - InferTy::IntVar(_) - | InferTy::FloatVar(_) - | InferTy::FreshIntTy(_) - | InferTy::FreshFloatTy(_) - ) + Bool | Char + | Str + | Int(_) + | Uint(_) + | Float(_) + | Infer( + InferTy::IntVar(_) + | InferTy::FloatVar(_) + | InferTy::FreshIntTy(_) + | InferTy::FreshFloatTy(_) + ) ) } @@ -245,8 +248,8 @@ pub fn suggest_constraining_type_param( } } - match ¶m_spans[..] { - &[¶m_span] => suggest_restrict(param_span.shrink_to_hi()), + match param_spans[..] { + [¶m_span] => suggest_restrict(param_span.shrink_to_hi()), _ => { err.span_suggestion_verbose( generics.where_clause.tail_span_for_suggestion(), diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index fc02e78b2f..1669c59d7f 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -1,7 +1,6 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; use crate::ty::diagnostics::suggest_constraining_type_param; use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; -use rustc_ast as ast; use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; use rustc_errors::{pluralize, DiagnosticBuilder}; use rustc_hir as hir; @@ -48,7 +47,7 @@ pub enum TypeError<'tcx> { Sorts(ExpectedFound>), IntMismatch(ExpectedFound), - FloatMismatch(ExpectedFound), + FloatMismatch(ExpectedFound), Traits(ExpectedFound), VariadicMismatch(ExpectedFound), @@ -647,14 +646,14 @@ impl Trait for X { let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. - let callable_scope = match body_owner { + let callable_scope = matches!( + body_owner, Some( hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) - | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) - | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), - ) => true, - _ => false, - }; + | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), + ) + ); let impl_comparison = matches!( cause_code, ObligationCauseCode::CompareImplMethodObligation { .. } @@ -832,7 +831,8 @@ fn foo(&self) -> Self::T { String::new() } } } Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl { items, .. }, .. + kind: hir::ItemKind::Impl(hir::Impl { items, .. }), + .. })) => { for item in &items[..] { if let hir::AssocItemKind::Type = item.kind { @@ -849,7 +849,7 @@ fn foo(&self) -> Self::T { String::new() } } /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` - /// requirement, provide a strucuted suggestion to constrain it to a given type `ty`. + /// requirement, provide a structured suggestion to constrain it to a given type `ty`. fn constrain_generic_bound_associated_type_structured_suggestion( self, db: &mut DiagnosticBuilder<'_>, diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 860f91db2b..94d75a469d 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -1,6 +1,5 @@ use crate::ich::StableHashingContext; use crate::ty::{self, Ty, TyCtxt}; -use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::DefId; use std::fmt::Debug; @@ -24,9 +23,9 @@ where { BoolSimplifiedType, CharSimplifiedType, - IntSimplifiedType(ast::IntTy), - UintSimplifiedType(ast::UintTy), - FloatSimplifiedType(ast::FloatTy), + IntSimplifiedType(ty::IntTy), + UintSimplifiedType(ty::UintTy), + FloatSimplifiedType(ty::FloatTy), AdtSimplifiedType(D), StrSimplifiedType, ArraySimplifiedType, diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 4de3d15924..6ecd1ebf37 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -22,9 +22,9 @@ impl FlagComputation { result } - pub fn for_predicate(kind: ty::PredicateKind<'_>) -> FlagComputation { + pub fn for_predicate(binder: ty::Binder>) -> FlagComputation { let mut result = FlagComputation::new(); - result.add_predicate_kind(kind); + result.add_predicate(binder); result } @@ -204,53 +204,46 @@ impl FlagComputation { } } - fn add_predicate_kind(&mut self, kind: ty::PredicateKind<'_>) { - match kind { - ty::PredicateKind::ForAll(binder) => { - self.bound_computation(binder, |computation, atom| { - computation.add_predicate_atom(atom) - }); - } - ty::PredicateKind::Atom(atom) => self.add_predicate_atom(atom), - } + fn add_predicate(&mut self, binder: ty::Binder>) { + self.bound_computation(binder, |computation, atom| computation.add_predicate_atom(atom)); } - fn add_predicate_atom(&mut self, atom: ty::PredicateAtom<'_>) { + fn add_predicate_atom(&mut self, atom: ty::PredicateKind<'_>) { match atom { - ty::PredicateAtom::Trait(trait_pred, _constness) => { + ty::PredicateKind::Trait(trait_pred, _constness) => { self.add_substs(trait_pred.trait_ref.substs); } - ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(a, b)) => { + ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => { self.add_region(a); self.add_region(b); } - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, region)) => { + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, region)) => { self.add_ty(ty); self.add_region(region); } - ty::PredicateAtom::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => { + ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => { self.add_ty(a); self.add_ty(b); } - ty::PredicateAtom::Projection(ty::ProjectionPredicate { projection_ty, ty }) => { + ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, ty }) => { self.add_projection_ty(projection_ty); self.add_ty(ty); } - ty::PredicateAtom::WellFormed(arg) => { + ty::PredicateKind::WellFormed(arg) => { self.add_substs(slice::from_ref(&arg)); } - ty::PredicateAtom::ObjectSafe(_def_id) => {} - ty::PredicateAtom::ClosureKind(_def_id, substs, _kind) => { + ty::PredicateKind::ObjectSafe(_def_id) => {} + ty::PredicateKind::ClosureKind(_def_id, substs, _kind) => { self.add_substs(substs); } - ty::PredicateAtom::ConstEvaluatable(_def_id, substs) => { + ty::PredicateKind::ConstEvaluatable(_def_id, substs) => { self.add_substs(substs); } - ty::PredicateAtom::ConstEquate(expected, found) => { + ty::PredicateKind::ConstEquate(expected, found) => { self.add_const(expected); self.add_const(found); } - ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { + ty::PredicateKind::TypeWellFormedFromEnv(ty) => { self.add_ty(ty); } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs b/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs index d9aebfc829..275384e227 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs @@ -3,6 +3,9 @@ use crate::ty::{DefId, DefIdTree}; use rustc_hir::CRATE_HIR_ID; use smallvec::SmallVec; use std::mem; +use std::sync::Arc; + +use DefIdForest::*; /// Represents a forest of `DefId`s closed under the ancestor relation. That is, /// if a `DefId` representing a module is contained in the forest then all @@ -11,45 +14,77 @@ use std::mem; /// /// This is used to represent a set of modules in which a type is visibly /// uninhabited. -#[derive(Clone)] -pub struct DefIdForest { - /// The minimal set of `DefId`s required to represent the whole set. - /// If A and B are DefIds in the `DefIdForest`, and A is a descendant - /// of B, then only B will be in `root_ids`. - /// We use a `SmallVec` here because (for its use for caching inhabitedness) - /// it's rare that this will contain even two IDs. - root_ids: SmallVec<[DefId; 1]>, +/// +/// We store the minimal set of `DefId`s required to represent the whole set. If A and B are +/// `DefId`s in the `DefIdForest`, and A is a parent of B, then only A will be stored. When this is +/// used with `type_uninhabited_from`, there will very rarely be more than one `DefId` stored. +#[derive(Clone, HashStable, Debug)] +pub enum DefIdForest { + Empty, + Single(DefId), + /// This variant is very rare. + /// Invariant: >1 elements + /// We use `Arc` because this is used in the output of a query. + Multiple(Arc<[DefId]>), +} + +/// Tests whether a slice of roots contains a given DefId. +#[inline] +fn slice_contains(tcx: TyCtxt<'tcx>, slice: &[DefId], id: DefId) -> bool { + slice.iter().any(|root_id| tcx.is_descendant_of(id, *root_id)) } impl<'tcx> DefIdForest { /// Creates an empty forest. pub fn empty() -> DefIdForest { - DefIdForest { root_ids: SmallVec::new() } + DefIdForest::Empty } /// Creates a forest consisting of a single tree representing the entire /// crate. #[inline] pub fn full(tcx: TyCtxt<'tcx>) -> DefIdForest { - let crate_id = tcx.hir().local_def_id(CRATE_HIR_ID); - DefIdForest::from_id(crate_id.to_def_id()) + DefIdForest::from_id(tcx.hir().local_def_id(CRATE_HIR_ID).to_def_id()) } /// Creates a forest containing a `DefId` and all its descendants. pub fn from_id(id: DefId) -> DefIdForest { - let mut root_ids = SmallVec::new(); - root_ids.push(id); - DefIdForest { root_ids } + DefIdForest::Single(id) + } + + fn as_slice(&self) -> &[DefId] { + match self { + Empty => &[], + Single(id) => std::slice::from_ref(id), + Multiple(root_ids) => root_ids, + } + } + + // Only allocates in the rare `Multiple` case. + fn from_slice(root_ids: &[DefId]) -> DefIdForest { + match root_ids { + [] => Empty, + [id] => Single(*id), + _ => DefIdForest::Multiple(root_ids.into()), + } } /// Tests whether the forest is empty. pub fn is_empty(&self) -> bool { - self.root_ids.is_empty() + match self { + Empty => true, + Single(..) | Multiple(..) => false, + } + } + + /// Iterate over the set of roots. + fn iter(&self) -> impl Iterator + '_ { + self.as_slice().iter().copied() } /// Tests whether the forest contains a given DefId. pub fn contains(&self, tcx: TyCtxt<'tcx>, id: DefId) -> bool { - self.root_ids.iter().any(|root_id| tcx.is_descendant_of(id, *root_id)) + slice_contains(tcx, self.as_slice(), id) } /// Calculate the intersection of a collection of forests. @@ -58,35 +93,28 @@ impl<'tcx> DefIdForest { I: IntoIterator, { let mut iter = iter.into_iter(); - let mut ret = if let Some(first) = iter.next() { - first + let mut ret: SmallVec<[_; 1]> = if let Some(first) = iter.next() { + SmallVec::from_slice(first.as_slice()) } else { return DefIdForest::full(tcx); }; - let mut next_ret = SmallVec::new(); - let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new(); + let mut next_ret: SmallVec<[_; 1]> = SmallVec::new(); for next_forest in iter { // No need to continue if the intersection is already empty. - if ret.is_empty() { - break; + if ret.is_empty() || next_forest.is_empty() { + return DefIdForest::empty(); } - for id in ret.root_ids.drain(..) { - if next_forest.contains(tcx, id) { - next_ret.push(id); - } else { - old_ret.push(id); - } - } - ret.root_ids.extend(old_ret.drain(..)); + // We keep the elements in `ret` that are also in `next_forest`. + next_ret.extend(ret.iter().copied().filter(|&id| next_forest.contains(tcx, id))); + // We keep the elements in `next_forest` that are also in `ret`. + next_ret.extend(next_forest.iter().filter(|&id| slice_contains(tcx, &ret, id))); - next_ret.extend(next_forest.root_ids.into_iter().filter(|&id| ret.contains(tcx, id))); - - mem::swap(&mut next_ret, &mut ret.root_ids); - next_ret.drain(..); + mem::swap(&mut next_ret, &mut ret); + next_ret.clear(); } - ret + DefIdForest::from_slice(&ret) } /// Calculate the union of a collection of forests. @@ -94,20 +122,26 @@ impl<'tcx> DefIdForest { where I: IntoIterator, { - let mut ret = DefIdForest::empty(); - let mut next_ret = SmallVec::new(); + let mut ret: SmallVec<[_; 1]> = SmallVec::new(); + let mut next_ret: SmallVec<[_; 1]> = SmallVec::new(); for next_forest in iter { - next_ret.extend(ret.root_ids.drain(..).filter(|&id| !next_forest.contains(tcx, id))); + // Union with the empty set is a no-op. + if next_forest.is_empty() { + continue; + } - for id in next_forest.root_ids { - if !next_ret.contains(&id) { + // We add everything in `ret` that is not in `next_forest`. + next_ret.extend(ret.iter().copied().filter(|&id| !next_forest.contains(tcx, id))); + // We add everything in `next_forest` that we haven't added yet. + for id in next_forest.iter() { + if !slice_contains(tcx, &next_ret, id) { next_ret.push(id); } } - mem::swap(&mut next_ret, &mut ret.root_ids); - next_ret.drain(..); + mem::swap(&mut next_ret, &mut ret); + next_ret.clear(); } - ret + DefIdForest::from_slice(&ret) } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 2f7707b949..119cb13504 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -6,7 +6,6 @@ use crate::ty::TyKind::*; use crate::ty::{AdtDef, FieldDef, Ty, TyS, VariantDef}; use crate::ty::{AdtKind, Visibility}; use crate::ty::{DefId, SubstsRef}; -use rustc_data_structures::stack::ensure_sufficient_stack; mod def_id_forest; @@ -187,34 +186,46 @@ impl<'tcx> FieldDef { impl<'tcx> TyS<'tcx> { /// Calculates the forest of `DefId`s from which this type is visibly uninhabited. - fn uninhabited_from(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> DefIdForest { - match *self.kind() { - Adt(def, substs) => { - ensure_sufficient_stack(|| def.uninhabited_from(tcx, substs, param_env)) - } + fn uninhabited_from( + &'tcx self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> DefIdForest { + tcx.type_uninhabited_from(param_env.and(self)) + } +} - Never => DefIdForest::full(tcx), +// Query provider for `type_uninhabited_from`. +pub(crate) fn type_uninhabited_from<'tcx>( + tcx: TyCtxt<'tcx>, + key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> DefIdForest { + let ty = key.value; + let param_env = key.param_env; + match *ty.kind() { + Adt(def, substs) => def.uninhabited_from(tcx, substs, param_env), - Tuple(ref tys) => DefIdForest::union( - tcx, - tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx, param_env)), - ), + Never => DefIdForest::full(tcx), - Array(ty, len) => match len.try_eval_usize(tcx, param_env) { - Some(0) | None => DefIdForest::empty(), - // If the array is definitely non-empty, it's uninhabited if - // the type of its elements is uninhabited. - Some(1..) => ty.uninhabited_from(tcx, param_env), - }, + Tuple(ref tys) => DefIdForest::union( + tcx, + tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx, param_env)), + ), - // References to uninitialised memory are valid for any type, including - // uninhabited types, in unsafe code, so we treat all references as - // inhabited. - // The precise semantics of inhabitedness with respect to references is currently - // undecided. - Ref(..) => DefIdForest::empty(), + Array(ty, len) => match len.try_eval_usize(tcx, param_env) { + Some(0) | None => DefIdForest::empty(), + // If the array is definitely non-empty, it's uninhabited if + // the type of its elements is uninhabited. + Some(1..) => ty.uninhabited_from(tcx, param_env), + }, - _ => DefIdForest::empty(), - } + // References to uninitialised memory are valid for any type, including + // uninhabited types, in unsafe code, so we treat all references as + // inhabited. + // The precise semantics of inhabitedness with respect to references is currently + // undecided. + Ref(..) => DefIdForest::empty(), + + _ => DefIdForest::empty(), } } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 413c9cca58..6ca5dcc532 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -535,7 +535,7 @@ fn polymorphize<'tcx>( } else { None }; - let has_upvars = upvars_ty.map(|ty| ty.tuple_fields().count() > 0).unwrap_or(false); + let has_upvars = upvars_ty.map_or(false, |ty| ty.tuple_fields().count() > 0); debug!("polymorphize: upvars_ty={:?} has_upvars={:?}", upvars_ty, has_upvars); struct PolymorphizationFolder<'tcx> { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index b545b92c92..596e4f6717 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -4,7 +4,7 @@ use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; use crate::ty::subst::Subst; use crate::ty::{self, subst::SubstsRef, ReprOptions, Ty, TyCtxt, TypeFoldable}; -use rustc_ast::{self as ast, IntTy, UintTy}; +use rustc_ast as ast; use rustc_attr as attr; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir as hir; @@ -30,6 +30,8 @@ use std::ops::Bound; pub trait IntegerExt { fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx>; fn from_attr(cx: &C, ity: attr::IntType) -> Integer; + fn from_int_ty(cx: &C, ity: ty::IntTy) -> Integer; + fn from_uint_ty(cx: &C, uty: ty::UintTy) -> Integer; fn repr_discr<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, @@ -60,17 +62,38 @@ impl IntegerExt for Integer { let dl = cx.data_layout(); match ity { - attr::SignedInt(IntTy::I8) | attr::UnsignedInt(UintTy::U8) => I8, - attr::SignedInt(IntTy::I16) | attr::UnsignedInt(UintTy::U16) => I16, - attr::SignedInt(IntTy::I32) | attr::UnsignedInt(UintTy::U32) => I32, - attr::SignedInt(IntTy::I64) | attr::UnsignedInt(UintTy::U64) => I64, - attr::SignedInt(IntTy::I128) | attr::UnsignedInt(UintTy::U128) => I128, - attr::SignedInt(IntTy::Isize) | attr::UnsignedInt(UintTy::Usize) => { + attr::SignedInt(ast::IntTy::I8) | attr::UnsignedInt(ast::UintTy::U8) => I8, + attr::SignedInt(ast::IntTy::I16) | attr::UnsignedInt(ast::UintTy::U16) => I16, + attr::SignedInt(ast::IntTy::I32) | attr::UnsignedInt(ast::UintTy::U32) => I32, + attr::SignedInt(ast::IntTy::I64) | attr::UnsignedInt(ast::UintTy::U64) => I64, + attr::SignedInt(ast::IntTy::I128) | attr::UnsignedInt(ast::UintTy::U128) => I128, + attr::SignedInt(ast::IntTy::Isize) | attr::UnsignedInt(ast::UintTy::Usize) => { dl.ptr_sized_integer() } } } + fn from_int_ty(cx: &C, ity: ty::IntTy) -> Integer { + match ity { + ty::IntTy::I8 => I8, + ty::IntTy::I16 => I16, + ty::IntTy::I32 => I32, + ty::IntTy::I64 => I64, + ty::IntTy::I128 => I128, + ty::IntTy::Isize => cx.data_layout().ptr_sized_integer(), + } + } + fn from_uint_ty(cx: &C, ity: ty::UintTy) -> Integer { + match ity { + ty::UintTy::U8 => I8, + ty::UintTy::U16 => I16, + ty::UintTy::U32 => I32, + ty::UintTy::U64 => I64, + ty::UintTy::U128 => I128, + ty::UintTy::Usize => cx.data_layout().ptr_sized_integer(), + } + } + /// Finds the appropriate Integer type and signedness for the given /// signed discriminant range and `#[repr]` attribute. /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but @@ -487,11 +510,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { self, Scalar { value: Int(I32, false), valid_range: 0..=0x10FFFF }, )), - ty::Int(ity) => scalar(Int(Integer::from_attr(dl, attr::SignedInt(ity)), true)), - ty::Uint(ity) => scalar(Int(Integer::from_attr(dl, attr::UnsignedInt(ity)), false)), + ty::Int(ity) => scalar(Int(Integer::from_int_ty(dl, ity), true)), + ty::Uint(ity) => scalar(Int(Integer::from_uint_ty(dl, ity), false)), ty::Float(fty) => scalar(match fty { - ast::FloatTy::F32 => F32, - ast::FloatTy::F64 => F64, + ty::FloatTy::F32 => F32, + ty::FloatTy::F64 => F64, }), ty::FnPtr(_) => { let mut ptr = scalar_unit(Pointer); @@ -1466,10 +1489,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { ) -> Result<&'tcx Layout, LayoutError<'tcx>> { use SavedLocalEligibility::*; let tcx = self.tcx; - let subst_field = |ty: Ty<'tcx>| ty.subst(tcx, substs); - let info = tcx.generator_layout(def_id); + let info = match tcx.generator_layout(def_id) { + None => return Err(LayoutError::Unknown(ty)), + Some(info) => info, + }; let (ineligible_locals, assignments) = self.generator_saved_local_eligibility(&info); // Build a prefix layout, including "promoting" all ineligible @@ -1634,7 +1659,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let layout = tcx.intern_layout(Layout { variants: Variants::Multiple { - tag: tag, + tag, tag_encoding: TagEncoding::Direct, tag_field: tag_index, variants, @@ -2512,7 +2537,7 @@ where extra_args: &[Ty<'tcx>], caller_location: Option>, codegen_fn_attr_flags: CodegenFnAttrFlags, - mk_arg_type: impl Fn(Ty<'tcx>, Option) -> ArgAbi<'tcx, Ty<'tcx>>, + make_self_ptr_thin: bool, ) -> Self; fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi); } @@ -2572,9 +2597,7 @@ where // Assume that fn pointers may always unwind let codegen_fn_attr_flags = CodegenFnAttrFlags::UNWIND; - call::FnAbi::new_internal(cx, sig, extra_args, None, codegen_fn_attr_flags, |ty, _| { - ArgAbi::new(cx.layout_of(ty)) - }) + call::FnAbi::new_internal(cx, sig, extra_args, None, codegen_fn_attr_flags, false) } fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self { @@ -2588,55 +2611,14 @@ where let attrs = cx.tcx().codegen_fn_attrs(instance.def_id()).flags; - call::FnAbi::new_internal(cx, sig, extra_args, caller_location, attrs, |ty, arg_idx| { - let mut layout = cx.layout_of(ty); - // Don't pass the vtable, it's not an argument of the virtual fn. - // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` - // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen - if let (ty::InstanceDef::Virtual(..), Some(0)) = (&instance.def, arg_idx) { - let fat_pointer_ty = if layout.is_unsized() { - // unsized `self` is passed as a pointer to `self` - // FIXME (mikeyhew) change this to use &own if it is ever added to the language - cx.tcx().mk_mut_ptr(layout.ty) - } else { - match layout.abi { - Abi::ScalarPair(..) => (), - _ => bug!("receiver type has unsupported layout: {:?}", layout), - } - - // In the case of Rc, we need to explicitly pass a *mut RcBox - // with a Scalar (not ScalarPair) ABI. This is a hack that is understood - // elsewhere in the compiler as a method on a `dyn Trait`. - // To get the type `*mut RcBox`, we just keep unwrapping newtypes until we - // get a built-in pointer type - let mut fat_pointer_layout = layout; - 'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr() - && !fat_pointer_layout.ty.is_region_ptr() - { - for i in 0..fat_pointer_layout.fields.count() { - let field_layout = fat_pointer_layout.field(cx, i); - - if !field_layout.is_zst() { - fat_pointer_layout = field_layout; - continue 'descend_newtypes; - } - } - - bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout); - } - - fat_pointer_layout.ty - }; - - // we now have a type like `*mut RcBox` - // change its layout to that of `*mut ()`, a thin pointer, but keep the same type - // this is understood as a special case elsewhere in the compiler - let unit_pointer_ty = cx.tcx().mk_mut_ptr(cx.tcx().mk_unit()); - layout = cx.layout_of(unit_pointer_ty); - layout.ty = fat_pointer_ty; - } - ArgAbi::new(layout) - }) + call::FnAbi::new_internal( + cx, + sig, + extra_args, + caller_location, + attrs, + matches!(instance.def, ty::InstanceDef::Virtual(..)), + ) } fn new_internal( @@ -2645,7 +2627,7 @@ where extra_args: &[Ty<'tcx>], caller_location: Option>, codegen_fn_attr_flags: CodegenFnAttrFlags, - mk_arg_type: impl Fn(Ty<'tcx>, Option) -> ArgAbi<'tcx, Ty<'tcx>>, + force_thin_self_ptr: bool, ) -> Self { debug!("FnAbi::new_internal({:?}, {:?})", sig, extra_args); @@ -2668,6 +2650,7 @@ where Win64 => Conv::X86_64Win64, SysV64 => Conv::X86_64SysV, Aapcs => Conv::ArmAapcs, + CCmseNonSecureCall => Conv::CCmseNonSecureCall, PtxKernel => Conv::PtxKernel, Msp430Interrupt => Conv::Msp430Intr, X86Interrupt => Conv::X86Intr, @@ -2776,7 +2759,23 @@ where let arg_of = |ty: Ty<'tcx>, arg_idx: Option| { let is_return = arg_idx.is_none(); - let mut arg = mk_arg_type(ty, arg_idx); + + let layout = cx.layout_of(ty); + let layout = if force_thin_self_ptr && arg_idx == Some(0) { + // Don't pass the vtable, it's not an argument of the virtual fn. + // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` + // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen + make_thin_self_ptr(cx, layout) + } else { + layout + }; + + let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| { + let mut attrs = ArgAttributes::new(); + adjust_for_rust_scalar(&mut attrs, scalar, *layout, offset, is_return); + attrs + }); + if arg.layout.is_zst() { // For some forsaken reason, x86_64-pc-windows-gnu // doesn't ignore zero-sized struct arguments. @@ -2792,30 +2791,6 @@ where } } - // FIXME(eddyb) other ABIs don't have logic for scalar pairs. - if !is_return && rust_abi { - if let Abi::ScalarPair(ref a, ref b) = arg.layout.abi { - let mut a_attrs = ArgAttributes::new(); - let mut b_attrs = ArgAttributes::new(); - adjust_for_rust_scalar(&mut a_attrs, a, arg.layout, Size::ZERO, false); - adjust_for_rust_scalar( - &mut b_attrs, - b, - arg.layout, - a.value.size(cx).align_to(b.value.align(cx).abi), - false, - ); - arg.mode = PassMode::Pair(a_attrs, b_attrs); - return arg; - } - } - - if let Abi::Scalar(ref scalar) = arg.layout.abi { - if let PassMode::Direct(ref mut attrs) = arg.mode { - adjust_for_rust_scalar(attrs, scalar, arg.layout, Size::ZERO, is_return); - } - } - arg }; @@ -2913,3 +2888,52 @@ where } } } + +fn make_thin_self_ptr<'tcx, C>(cx: &C, mut layout: TyAndLayout<'tcx>) -> TyAndLayout<'tcx> +where + C: LayoutOf, TyAndLayout = TyAndLayout<'tcx>> + + HasTyCtxt<'tcx> + + HasParamEnv<'tcx>, +{ + let fat_pointer_ty = if layout.is_unsized() { + // unsized `self` is passed as a pointer to `self` + // FIXME (mikeyhew) change this to use &own if it is ever added to the language + cx.tcx().mk_mut_ptr(layout.ty) + } else { + match layout.abi { + Abi::ScalarPair(..) => (), + _ => bug!("receiver type has unsupported layout: {:?}", layout), + } + + // In the case of Rc, we need to explicitly pass a *mut RcBox + // with a Scalar (not ScalarPair) ABI. This is a hack that is understood + // elsewhere in the compiler as a method on a `dyn Trait`. + // To get the type `*mut RcBox`, we just keep unwrapping newtypes until we + // get a built-in pointer type + let mut fat_pointer_layout = layout; + 'descend_newtypes: while !fat_pointer_layout.ty.is_unsafe_ptr() + && !fat_pointer_layout.ty.is_region_ptr() + { + for i in 0..fat_pointer_layout.fields.count() { + let field_layout = fat_pointer_layout.field(cx, i); + + if !field_layout.is_zst() { + fat_pointer_layout = field_layout; + continue 'descend_newtypes; + } + } + + bug!("receiver has no non-zero-sized fields {:?}", fat_pointer_layout); + } + + fat_pointer_layout.ty + }; + + // we now have a type like `*mut RcBox` + // change its layout to that of `*mut ()`, a thin pointer, but keep the same type + // this is understood as a special case elsewhere in the compiler + let unit_pointer_ty = cx.tcx().mk_mut_ptr(cx.tcx().mk_unit()); + layout = cx.layout_of(unit_pointer_ty); + layout.ty = fat_pointer_ty; + layout +} diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8395692446..babab005ed 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -17,7 +17,9 @@ pub use self::IntVarValue::*; pub use self::Variance::*; use crate::hir::exports::ExportMap; -use crate::hir::place::Place as HirPlace; +use crate::hir::place::{ + Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, +}; use crate::ich::StableHashingContext; use crate::middle::cstore::CrateStoreDyn; use crate::middle::resolve_lifetime::ObjectLifetimeDefault; @@ -63,7 +65,6 @@ use std::ptr; use std::str; pub use self::sty::BoundRegionKind::*; -pub use self::sty::InferTy::*; pub use self::sty::RegionKind; pub use self::sty::RegionKind::*; pub use self::sty::TyKind::*; @@ -72,13 +73,14 @@ pub use self::sty::{BoundRegion, BoundRegionKind, EarlyBoundRegion, FreeRegion, pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig}; pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts}; pub use self::sty::{ClosureSubstsParts, GeneratorSubstsParts}; -pub use self::sty::{ConstVid, FloatVid, IntVid, RegionVid, TyVid}; -pub use self::sty::{ExistentialPredicate, InferTy, ParamConst, ParamTy, ProjectionTy}; +pub use self::sty::{ConstVid, RegionVid}; +pub use self::sty::{ExistentialPredicate, ParamConst, ParamTy, ProjectionTy}; pub use self::sty::{ExistentialProjection, PolyExistentialProjection}; pub use self::sty::{ExistentialTraitRef, PolyExistentialTraitRef}; pub use self::sty::{PolyTraitRef, TraitRef, TyKind}; pub use crate::ty::diagnostics::*; -pub use rustc_type_ir::{DebruijnIndex, TypeFlags, INNERMOST}; +pub use rustc_type_ir::InferTy::*; +pub use rustc_type_ir::*; pub use self::binding::BindingMode; pub use self::binding::BindingMode::*; @@ -183,7 +185,7 @@ pub struct ImplHeader<'tcx> { pub predicates: Vec>, } -#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)] pub enum ImplPolarity { /// `impl Trait for Type` Positive, @@ -419,21 +421,13 @@ impl Visibility { } } -#[derive(Copy, Clone, PartialEq, TyDecodable, TyEncodable, HashStable)] -pub enum Variance { - Covariant, // T <: T iff A <: B -- e.g., function return type - Invariant, // T <: T iff B == A -- e.g., type of mutable cell - Contravariant, // T <: T iff B <: A -- e.g., function param type - Bivariant, // T <: T -- e.g., unused type parameter -} - /// The crate variances map is computed during typeck and contains the /// variance of every item in the local crate. You should not use it /// directly, because to do so will make your pass dependent on the /// HIR of every item in the local crate. Instead, use /// `tcx.variances_of()` to get the variance for a *particular* /// item. -#[derive(HashStable)] +#[derive(HashStable, Debug)] pub struct CrateVariancesMap<'tcx> { /// For each item with generics, maps to a vector of the variance /// of its generics. If an item has no generics, it will have no @@ -441,66 +435,6 @@ pub struct CrateVariancesMap<'tcx> { pub variances: FxHashMap, } -impl Variance { - /// `a.xform(b)` combines the variance of a context with the - /// variance of a type with the following meaning. If we are in a - /// context with variance `a`, and we encounter a type argument in - /// a position with variance `b`, then `a.xform(b)` is the new - /// variance with which the argument appears. - /// - /// Example 1: - /// - /// *mut Vec - /// - /// Here, the "ambient" variance starts as covariant. `*mut T` is - /// invariant with respect to `T`, so the variance in which the - /// `Vec` appears is `Covariant.xform(Invariant)`, which - /// yields `Invariant`. Now, the type `Vec` is covariant with - /// respect to its type argument `T`, and hence the variance of - /// the `i32` here is `Invariant.xform(Covariant)`, which results - /// (again) in `Invariant`. - /// - /// Example 2: - /// - /// fn(*const Vec, *mut Vec` appears is - /// `Contravariant.xform(Covariant)` or `Contravariant`. The same - /// is true for its `i32` argument. In the `*mut T` case, the - /// variance of `Vec` is `Contravariant.xform(Invariant)`, - /// and hence the outermost type is `Invariant` with respect to - /// `Vec` (and its `i32` argument). - /// - /// Source: Figure 1 of "Taming the Wildcards: - /// Combining Definition- and Use-Site Variance" published in PLDI'11. - pub fn xform(self, v: ty::Variance) -> ty::Variance { - match (self, v) { - // Figure 1, column 1. - (ty::Covariant, ty::Covariant) => ty::Covariant, - (ty::Covariant, ty::Contravariant) => ty::Contravariant, - (ty::Covariant, ty::Invariant) => ty::Invariant, - (ty::Covariant, ty::Bivariant) => ty::Bivariant, - - // Figure 1, column 2. - (ty::Contravariant, ty::Covariant) => ty::Contravariant, - (ty::Contravariant, ty::Contravariant) => ty::Covariant, - (ty::Contravariant, ty::Invariant) => ty::Invariant, - (ty::Contravariant, ty::Bivariant) => ty::Bivariant, - - // Figure 1, column 3. - (ty::Invariant, _) => ty::Invariant, - - // Figure 1, column 4. - (ty::Bivariant, _) => ty::Bivariant, - } - } -} - // Contains information needed to resolve types and (in the future) look up // the types of AST nodes. #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -727,11 +661,65 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap = Vec>; -/// A `Place` and the corresponding `CaptureInfo`. +/// A composite describing a `Place` that is captured by a closure. #[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct CapturedPlace<'tcx> { + /// The `Place` that is captured. pub place: HirPlace<'tcx>, + + /// `CaptureKind` and expression(s) that resulted in such capture of `place`. pub info: CaptureInfo<'tcx>, + + /// Represents if `place` can be mutated or not. + pub mutability: hir::Mutability, +} + +impl CapturedPlace<'tcx> { + /// Returns the hir-id of the root variable for the captured place. + /// e.g., if `a.b.c` was captured, would return the hir-id for `a`. + pub fn get_root_variable(&self) -> hir::HirId { + match self.place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + base => bug!("Expected upvar, found={:?}", base), + } + } +} + +pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String { + let name = match place.base { + HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(), + _ => bug!("Capture_information should only contain upvars"), + }; + let mut curr_string = name; + + for (i, proj) in place.projections.iter().enumerate() { + match proj.kind { + HirProjectionKind::Deref => { + curr_string = format!("*{}", curr_string); + } + HirProjectionKind::Field(idx, variant) => match place.ty_before_projection(i).kind() { + ty::Adt(def, ..) => { + curr_string = format!( + "{}.{}", + curr_string, + def.variants[variant].fields[idx as usize].ident.name.as_str() + ); + } + ty::Tuple(_) => { + curr_string = format!("{}.{}", curr_string, idx); + } + _ => { + bug!( + "Field projection applied to a type other than Adt or Tuple: {:?}.", + place.ty_before_projection(i).kind() + ) + } + }, + proj => bug!("{:?} unexpected because it isn't captured", proj), + } + } + + curr_string.to_string() } /// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) @@ -741,8 +729,20 @@ pub struct CapturedPlace<'tcx> { pub struct CaptureInfo<'tcx> { /// Expr Id pointing to use that resulted in selecting the current capture kind /// + /// Eg: + /// ```rust,no_run + /// let mut t = (0,1); + /// + /// let c = || { + /// println!("{}",t); // L1 + /// t.1 = 4; // L2 + /// }; + /// ``` + /// `capture_kind_expr_id` will point to the use on L2 and `path_expr_id` will point to the + /// use on L1. + /// /// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is - /// possible that we don't see the use of a particular place resulting in expr_id being + /// possible that we don't see the use of a particular place resulting in capture_kind_expr_id being /// None. In such case we fallback on uvpars_mentioned for span. /// /// Eg: @@ -756,7 +756,12 @@ pub struct CaptureInfo<'tcx> { /// /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured, /// but we won't see it being used during capture analysis, since it's essentially a discard. - pub expr_id: Option, + pub capture_kind_expr_id: Option, + /// Expr Id pointing to use that resulted the corresponding place being captured + /// + /// See `capture_kind_expr_id` for example. + /// + pub path_expr_id: Option, /// Capture mode that was selected pub capture_kind: UpvarCapture<'tcx>, @@ -765,15 +770,6 @@ pub struct CaptureInfo<'tcx> { pub type UpvarListMap = FxHashMap>; pub type UpvarCaptureMap<'tcx> = FxHashMap>; -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum IntVarValue { - IntType(ast::IntTy), - UintType(ast::UintTy), -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct FloatVarValue(pub ast::FloatTy); - impl ty::EarlyBoundRegion { /// Does this early bound region have a name? Early bound regions normally /// always have names except when using anonymous lifetimes (`'_`). @@ -801,6 +797,15 @@ impl GenericParamDefKind { GenericParamDefKind::Const => "constant", } } + pub fn to_ord(&self, tcx: TyCtxt<'_>) -> ast::ParamKindOrd { + match self { + GenericParamDefKind::Lifetime => ast::ParamKindOrd::Lifetime, + GenericParamDefKind::Type { .. } => ast::ParamKindOrd::Type, + GenericParamDefKind::Const => { + ast::ParamKindOrd::Const { unordered: tcx.features().const_generics } + } + } + } } #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] @@ -862,19 +867,37 @@ impl<'tcx> Generics { // We could cache this as a property of `GenericParamCount`, but // the aim is to refactor this away entirely eventually and the // presence of this method will be a constant reminder. - let mut own_counts: GenericParamCount = Default::default(); + let mut own_counts = GenericParamCount::default(); for param in &self.params { match param.kind { GenericParamDefKind::Lifetime => own_counts.lifetimes += 1, GenericParamDefKind::Type { .. } => own_counts.types += 1, GenericParamDefKind::Const => own_counts.consts += 1, - }; + } } own_counts } + pub fn own_defaults(&self) -> GenericParamCount { + let mut own_defaults = GenericParamCount::default(); + + for param in &self.params { + match param.kind { + GenericParamDefKind::Lifetime => (), + GenericParamDefKind::Type { has_default, .. } => { + own_defaults.types += has_default as usize; + } + GenericParamDefKind::Const => { + // FIXME(const_generics:defaults) + } + } + } + + own_defaults + } + pub fn requires_monomorphization(&self, tcx: TyCtxt<'tcx>) -> bool { if self.own_requires_monomorphization() { return true; @@ -1003,14 +1026,14 @@ impl<'tcx> GenericPredicates<'tcx> { #[derive(Debug)] crate struct PredicateInner<'tcx> { - kind: PredicateKind<'tcx>, + kind: Binder>, flags: TypeFlags, /// See the comment for the corresponding field of [TyS]. outer_exclusive_binder: ty::DebruijnIndex, } #[cfg(target_arch = "x86_64")] -static_assert_size!(PredicateInner<'_>, 48); +static_assert_size!(PredicateInner<'_>, 40); #[derive(Clone, Copy, Lift)] pub struct Predicate<'tcx> { @@ -1033,59 +1056,9 @@ impl Hash for Predicate<'_> { impl<'tcx> Eq for Predicate<'tcx> {} impl<'tcx> Predicate<'tcx> { - #[inline(always)] - pub fn kind(self) -> &'tcx PredicateKind<'tcx> { - &self.inner.kind - } - - /// Returns the inner `PredicateAtom`. - /// - /// The returned atom may contain unbound variables bound to binders skipped in this method. - /// It is safe to reapply binders to the given atom. - /// - /// Note that this method panics in case this predicate has unbound variables. - pub fn skip_binders(self) -> PredicateAtom<'tcx> { - match self.kind() { - &PredicateKind::ForAll(binder) => binder.skip_binder(), - &PredicateKind::Atom(atom) => { - debug_assert!(!atom.has_escaping_bound_vars()); - atom - } - } - } - - /// Returns the inner `PredicateAtom`. - /// - /// Note that this method does not check if the predicate has unbound variables. - /// - /// Rebinding the returned atom can causes the previously bound variables - /// to end up at the wrong binding level. - pub fn skip_binders_unchecked(self) -> PredicateAtom<'tcx> { - match self.kind() { - &PredicateKind::ForAll(binder) => binder.skip_binder(), - &PredicateKind::Atom(atom) => atom, - } - } - - /// Converts this to a `Binder>`. If the value was an - /// `Atom`, then it is not allowed to contain escaping bound vars. - pub fn bound_atom(self) -> Binder> { - match self.kind() { - &PredicateKind::ForAll(binder) => binder, - &PredicateKind::Atom(atom) => { - debug_assert!(!atom.has_escaping_bound_vars()); - Binder::dummy(atom) - } - } - } - - /// Allows using a `Binder>` even if the given predicate previously - /// contained unbound variables by shifting these variables outwards. - pub fn bound_atom_with_opt_escaping(self, tcx: TyCtxt<'tcx>) -> Binder> { - match self.kind() { - &PredicateKind::ForAll(binder) => binder, - &PredicateKind::Atom(atom) => Binder::wrap_nonbinding(tcx, atom), - } + /// Gets the inner `Binder>`. + pub fn kind(self) -> Binder> { + self.inner.kind } } @@ -1107,14 +1080,6 @@ impl<'a, 'tcx> HashStable> for Predicate<'tcx> { #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub enum PredicateKind<'tcx> { - /// `for<'a>: ...` - ForAll(Binder>), - Atom(PredicateAtom<'tcx>), -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable)] -pub enum PredicateAtom<'tcx> { /// Corresponds to `where Foo: Bar`. `Foo` here would be /// the `Self` type of the trait reference and `A`, `B`, and `C` /// would be the type parameters. @@ -1160,28 +1125,13 @@ pub enum PredicateAtom<'tcx> { TypeWellFormedFromEnv(Ty<'tcx>), } -impl<'tcx> Binder> { - /// Wraps `self` with the given qualifier if this predicate has any unbound variables. - pub fn potentially_quantified( - self, - tcx: TyCtxt<'tcx>, - qualifier: impl FnOnce(Binder>) -> PredicateKind<'tcx>, - ) -> Predicate<'tcx> { - match self.no_bound_vars() { - Some(atom) => PredicateKind::Atom(atom), - None => qualifier(self), - } - .to_predicate(tcx) - } -} - /// The crate outlives map is computed during typeck and contains the /// outlives of every item in the local crate. You should not use it /// directly, because to do so will make your pass dependent on the /// HIR of every item in the local crate. Instead, use /// `tcx.inferred_outlives_of()` to get the outlives for a *particular* /// item. -#[derive(HashStable)] +#[derive(HashStable, Debug)] pub struct CratePredicatesMap<'tcx> { /// For each struct with outlive bounds, maps to a vector of the /// predicate of its outlive bounds. If an item has no outlives @@ -1260,13 +1210,9 @@ impl<'tcx> Predicate<'tcx> { // from the substitution and the value being substituted into, and // this trick achieves that). let substs = trait_ref.skip_binder().substs; - let pred = self.skip_binders(); + let pred = self.kind().skip_binder(); let new = pred.subst(tcx, substs); - if new != pred { - ty::Binder::bind(new).potentially_quantified(tcx, PredicateKind::ForAll) - } else { - self - } + tcx.reuse_or_mk_predicate(self, ty::Binder::bind(new)) } } @@ -1387,24 +1333,23 @@ pub trait ToPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx>; } -impl ToPredicate<'tcx> for PredicateKind<'tcx> { +impl ToPredicate<'tcx> for Binder> { #[inline(always)] fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { tcx.mk_predicate(self) } } -impl ToPredicate<'tcx> for PredicateAtom<'tcx> { +impl ToPredicate<'tcx> for PredicateKind<'tcx> { #[inline(always)] fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - debug_assert!(!self.has_escaping_bound_vars(), "escaping bound vars for {:?}", self); - tcx.mk_predicate(PredicateKind::Atom(self)) + tcx.mk_predicate(Binder::dummy(self)) } } impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - PredicateAtom::Trait(ty::TraitPredicate { trait_ref: self.value }, self.constness) + PredicateKind::Trait(ty::TraitPredicate { trait_ref: self.value }, self.constness) .to_predicate(tcx) } } @@ -1421,67 +1366,62 @@ impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - self.value - .map_bound(|value| PredicateAtom::Trait(value, self.constness)) - .potentially_quantified(tcx, PredicateKind::ForAll) + self.value.map_bound(|value| PredicateKind::Trait(value, self.constness)).to_predicate(tcx) } } impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - self.map_bound(|value| PredicateAtom::RegionOutlives(value)) - .potentially_quantified(tcx, PredicateKind::ForAll) + self.map_bound(PredicateKind::RegionOutlives).to_predicate(tcx) } } impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - self.map_bound(|value| PredicateAtom::TypeOutlives(value)) - .potentially_quantified(tcx, PredicateKind::ForAll) + self.map_bound(PredicateKind::TypeOutlives).to_predicate(tcx) } } impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { - self.map_bound(|value| PredicateAtom::Projection(value)) - .potentially_quantified(tcx, PredicateKind::ForAll) + self.map_bound(PredicateKind::Projection).to_predicate(tcx) } } impl<'tcx> Predicate<'tcx> { pub fn to_opt_poly_trait_ref(self) -> Option>> { - let predicate = self.bound_atom(); + let predicate = self.kind(); match predicate.skip_binder() { - PredicateAtom::Trait(t, constness) => { + PredicateKind::Trait(t, constness) => { Some(ConstnessAnd { constness, value: predicate.rebind(t.trait_ref) }) } - PredicateAtom::Projection(..) - | PredicateAtom::Subtype(..) - | PredicateAtom::RegionOutlives(..) - | PredicateAtom::WellFormed(..) - | PredicateAtom::ObjectSafe(..) - | PredicateAtom::ClosureKind(..) - | PredicateAtom::TypeOutlives(..) - | PredicateAtom::ConstEvaluatable(..) - | PredicateAtom::ConstEquate(..) - | PredicateAtom::TypeWellFormedFromEnv(..) => None, + PredicateKind::Projection(..) + | PredicateKind::Subtype(..) + | PredicateKind::RegionOutlives(..) + | PredicateKind::WellFormed(..) + | PredicateKind::ObjectSafe(..) + | PredicateKind::ClosureKind(..) + | PredicateKind::TypeOutlives(..) + | PredicateKind::ConstEvaluatable(..) + | PredicateKind::ConstEquate(..) + | PredicateKind::TypeWellFormedFromEnv(..) => None, } } pub fn to_opt_type_outlives(self) -> Option> { - let predicate = self.bound_atom(); + let predicate = self.kind(); match predicate.skip_binder() { - PredicateAtom::TypeOutlives(data) => Some(predicate.rebind(data)), - PredicateAtom::Trait(..) - | PredicateAtom::Projection(..) - | PredicateAtom::Subtype(..) - | PredicateAtom::RegionOutlives(..) - | PredicateAtom::WellFormed(..) - | PredicateAtom::ObjectSafe(..) - | PredicateAtom::ClosureKind(..) - | PredicateAtom::ConstEvaluatable(..) - | PredicateAtom::ConstEquate(..) - | PredicateAtom::TypeWellFormedFromEnv(..) => None, + PredicateKind::TypeOutlives(data) => Some(predicate.rebind(data)), + PredicateKind::Trait(..) + | PredicateKind::Projection(..) + | PredicateKind::Subtype(..) + | PredicateKind::RegionOutlives(..) + | PredicateKind::WellFormed(..) + | PredicateKind::ObjectSafe(..) + | PredicateKind::ClosureKind(..) + | PredicateKind::ConstEvaluatable(..) + | PredicateKind::ConstEquate(..) + | PredicateKind::TypeWellFormedFromEnv(..) => None, } } } @@ -1638,8 +1578,6 @@ pub type PlaceholderConst<'tcx> = Placeholder>; /// which cause cycle errors. /// /// ```rust -/// #![feature(const_generics)] -/// /// struct A; /// impl A { /// fn foo(&self) -> [u8; N] { [0; N] } @@ -2890,19 +2828,11 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn opt_associated_item(self, def_id: DefId) -> Option<&'tcx AssocItem> { - let is_associated_item = if let Some(def_id) = def_id.as_local() { - matches!( - self.hir().get(self.hir().local_def_id_to_hir_id(def_id)), - Node::TraitItem(_) | Node::ImplItem(_) - ) + if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { + Some(self.associated_item(def_id)) } else { - matches!( - self.def_kind(def_id), - DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy - ) - }; - - is_associated_item.then(|| self.associated_item(def_id)) + None + } } pub fn field_index(self, hir_id: hir::HirId, typeck_results: &TypeckResults<'_>) -> usize { @@ -3012,7 +2942,16 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair. pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> { match instance { - ty::InstanceDef::Item(def) => self.optimized_mir_opt_const_arg(def), + ty::InstanceDef::Item(def) => match self.def_kind(def.did) { + DefKind::Const + | DefKind::Static + | DefKind::AssocConst + | DefKind::Ctor(..) + | DefKind::AnonConst => self.mir_for_ctfe_opt_const_arg(def), + // If the caller wants `mir_for_ctfe` of a function they should not be using + // `instance_mir`, so we'll assume const fn also wants the optimized version. + _ => self.optimized_mir_or_const_arg_mir(def), + }, ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::Intrinsic(..) @@ -3043,8 +2982,10 @@ impl<'tcx> TyCtxt<'tcx> { self.trait_def(trait_def_id).has_auto_impl } - pub fn generator_layout(self, def_id: DefId) -> &'tcx GeneratorLayout<'tcx> { - self.optimized_mir(def_id).generator_layout.as_ref().unwrap() + /// Returns layout of a generator. Layout might be unavailable if the + /// generator is tainted by errors. + pub fn generator_layout(self, def_id: DefId) -> Option<&'tcx GeneratorLayout<'tcx>> { + self.optimized_mir(def_id).generator_layout.as_ref() } /// Given the `DefId` of an impl, returns the `DefId` of the trait it implements. @@ -3123,7 +3064,7 @@ impl<'tcx> TyCtxt<'tcx> { } } -#[derive(Clone, HashStable)] +#[derive(Clone, HashStable, Debug)] pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]); /// Yields the parent function's `DefId` if `def_id` is an `impl Trait` definition. @@ -3138,6 +3079,57 @@ pub fn is_impl_trait_defn(tcx: TyCtxt<'_>, def_id: DefId) -> Option { None } +pub fn int_ty(ity: ast::IntTy) -> IntTy { + match ity { + ast::IntTy::Isize => IntTy::Isize, + ast::IntTy::I8 => IntTy::I8, + ast::IntTy::I16 => IntTy::I16, + ast::IntTy::I32 => IntTy::I32, + ast::IntTy::I64 => IntTy::I64, + ast::IntTy::I128 => IntTy::I128, + } +} + +pub fn uint_ty(uty: ast::UintTy) -> UintTy { + match uty { + ast::UintTy::Usize => UintTy::Usize, + ast::UintTy::U8 => UintTy::U8, + ast::UintTy::U16 => UintTy::U16, + ast::UintTy::U32 => UintTy::U32, + ast::UintTy::U64 => UintTy::U64, + ast::UintTy::U128 => UintTy::U128, + } +} + +pub fn float_ty(fty: ast::FloatTy) -> FloatTy { + match fty { + ast::FloatTy::F32 => FloatTy::F32, + ast::FloatTy::F64 => FloatTy::F64, + } +} + +pub fn ast_int_ty(ity: IntTy) -> ast::IntTy { + match ity { + IntTy::Isize => ast::IntTy::Isize, + IntTy::I8 => ast::IntTy::I8, + IntTy::I16 => ast::IntTy::I16, + IntTy::I32 => ast::IntTy::I32, + IntTy::I64 => ast::IntTy::I64, + IntTy::I128 => ast::IntTy::I128, + } +} + +pub fn ast_uint_ty(uty: UintTy) -> ast::UintTy { + match uty { + UintTy::Usize => ast::UintTy::Usize, + UintTy::U8 => ast::UintTy::U8, + UintTy::U16 => ast::UintTy::U16, + UintTy::U32 => ast::UintTy::U32, + UintTy::U64 => ast::UintTy::U64, + UintTy::U128 => ast::UintTy::U128, + } +} + pub fn provide(providers: &mut ty::query::Providers) { context::provide(providers); erase_regions::provide(providers); @@ -3148,6 +3140,7 @@ pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { trait_impls_of: trait_def::trait_impls_of_provider, all_local_trait_impls: trait_def::all_local_trait_impls, + type_uninhabited_from: inhabitedness::type_uninhabited_from, ..*providers }; } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index c79e06b7fd..77f1668893 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -203,7 +203,7 @@ pub trait Printer<'tcx>: Sized { self.tcx().type_of(param.def_id).subst(self.tcx(), substs), ) } - ty::GenericParamDefKind::Const => false, // FIXME(const_generics:defaults) + ty::GenericParamDefKind::Const => false, // FIXME(const_generics_defaults) } }) .count(); diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 9b178d9d2b..4937fdd731 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3,7 +3,6 @@ use crate::mir::interpret::{AllocId, ConstValue, GlobalAlloc, Pointer, Scalar}; use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; use crate::ty::{self, ConstInt, DefIdTree, ParamConst, ScalarInt, Ty, TyCtxt, TypeFoldable}; use rustc_apfloat::ieee::{Double, Single}; -use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; @@ -557,14 +556,19 @@ pub trait PrettyPrinter<'tcx>: } ty::FnPtr(ref bare_fn) => p!(print(bare_fn)), ty::Infer(infer_ty) => { + let verbose = self.tcx().sess.verbose(); if let ty::TyVar(ty_vid) = infer_ty { if let Some(name) = self.infer_ty_name(ty_vid) { p!(write("{}", name)) } else { - p!(write("{}", infer_ty)) + if verbose { + p!(write("{:?}", infer_ty)) + } else { + p!(write("{}", infer_ty)) + } } } else { - p!(write("{}", infer_ty)) + if verbose { p!(write("{:?}", infer_ty)) } else { p!(write("{}", infer_ty)) } } } ty::Error(_) => p!("[type error]"), @@ -623,12 +627,8 @@ pub trait PrettyPrinter<'tcx>: p!("impl"); for (predicate, _) in bounds { let predicate = predicate.subst(self.tcx(), substs); - // Note: We can't use `to_opt_poly_trait_ref` here as `predicate` - // may contain unbound variables. We therefore do this manually. - // - // FIXME(lcnr): Find out why exactly this is the case :) - let bound_predicate = predicate.bound_atom_with_opt_escaping(self.tcx()); - if let ty::PredicateAtom::Trait(pred, _) = bound_predicate.skip_binder() { + let bound_predicate = predicate.kind(); + if let ty::PredicateKind::Trait(pred, _) = bound_predicate.skip_binder() { let trait_ref = bound_predicate.rebind(pred.trait_ref); // Don't print +Sized, but rather +?Sized if absent. if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() { @@ -972,7 +972,7 @@ pub trait PrettyPrinter<'tcx>: ty::TyS { kind: ty::Array( - ty::TyS { kind: ty::Uint(ast::UintTy::U8), .. }, + ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. }, ty::Const { val: ty::ConstKind::Value(ConstValue::Scalar(int)), .. @@ -1001,10 +1001,10 @@ pub trait PrettyPrinter<'tcx>: (Scalar::Int(int), ty::Bool) if int == ScalarInt::FALSE => p!("false"), (Scalar::Int(int), ty::Bool) if int == ScalarInt::TRUE => p!("true"), // Float - (Scalar::Int(int), ty::Float(ast::FloatTy::F32)) => { + (Scalar::Int(int), ty::Float(ty::FloatTy::F32)) => { p!(write("{}f32", Single::try_from(int).unwrap())) } - (Scalar::Int(int), ty::Float(ast::FloatTy::F64)) => { + (Scalar::Int(int), ty::Float(ty::FloatTy::F64)) => { p!(write("{}f64", Double::try_from(int).unwrap())) } // Int @@ -1250,7 +1250,7 @@ pub struct FmtPrinterData<'a, 'tcx, F> { pub region_highlight_mode: RegionHighlightMode, - pub name_resolver: Option Option>>, + pub name_resolver: Option Option>>, } impl Deref for FmtPrinter<'a, 'tcx, F> { @@ -1481,7 +1481,7 @@ impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { // FIXME(eddyb) `name` should never be empty, but it // currently is for `extern { ... }` "foreign modules". let name = disambiguated_data.data.name(); - if name != DefPathDataName::Named(kw::Invalid) { + if name != DefPathDataName::Named(kw::Empty) { if !self.empty_path { write!(self, "::")?; } @@ -1608,14 +1608,14 @@ impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { match *region { ty::ReEarlyBound(ref data) => { - data.name != kw::Invalid && data.name != kw::UnderscoreLifetime + data.name != kw::Empty && data.name != kw::UnderscoreLifetime } ty::ReLateBound(_, ty::BoundRegion { kind: br }) | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { if let ty::BrNamed(_, name) = br { - if name != kw::Invalid && name != kw::UnderscoreLifetime { + if name != kw::Empty && name != kw::UnderscoreLifetime { return true; } } @@ -1685,7 +1685,7 @@ impl FmtPrinter<'_, '_, F> { // `explain_region()` or `note_and_explain_region()`. match *region { ty::ReEarlyBound(ref data) => { - if data.name != kw::Invalid { + if data.name != kw::Empty { p!(write("{}", data.name)); return Ok(self); } @@ -1694,7 +1694,7 @@ impl FmtPrinter<'_, '_, F> { | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { if let ty::BrNamed(_, name) = br { - if name != kw::Invalid && name != kw::UnderscoreLifetime { + if name != kw::Empty && name != kw::UnderscoreLifetime { p!(write("{}", name)); return Ok(self); } @@ -2011,21 +2011,6 @@ define_print_and_forward_display! { p!("fn", pretty_fn_sig(self.inputs(), self.c_variadic, self.output())); } - ty::InferTy { - if cx.tcx().sess.verbose() { - p!(write("{:?}", self)); - return Ok(cx); - } - match *self { - ty::TyVar(_) => p!("_"), - ty::IntVar(_) => p!(write("{}", "{integer}")), - ty::FloatVar(_) => p!(write("{}", "{float}")), - ty::FreshTy(v) => p!(write("FreshTy({})", v)), - ty::FreshIntTy(v) => p!(write("FreshIntTy({})", v)), - ty::FreshFloatTy(v) => p!(write("FreshFloatTy({})", v)) - } - } - ty::TraitRef<'tcx> { p!(write("<{} as {}>", self.self_ty(), self.print_only_trait_path())) } @@ -2068,40 +2053,38 @@ define_print_and_forward_display! { } ty::Predicate<'tcx> { - match self.kind() { - &ty::PredicateKind::Atom(atom) => p!(print(atom)), - ty::PredicateKind::ForAll(binder) => p!(print(binder)), - } + let binder = self.kind(); + p!(print(binder)) } - ty::PredicateAtom<'tcx> { + ty::PredicateKind<'tcx> { match *self { - ty::PredicateAtom::Trait(ref data, constness) => { + ty::PredicateKind::Trait(ref data, constness) => { if let hir::Constness::Const = constness { p!("const "); } p!(print(data)) } - ty::PredicateAtom::Subtype(predicate) => p!(print(predicate)), - ty::PredicateAtom::RegionOutlives(predicate) => p!(print(predicate)), - ty::PredicateAtom::TypeOutlives(predicate) => p!(print(predicate)), - ty::PredicateAtom::Projection(predicate) => p!(print(predicate)), - ty::PredicateAtom::WellFormed(arg) => p!(print(arg), " well-formed"), - ty::PredicateAtom::ObjectSafe(trait_def_id) => { + ty::PredicateKind::Subtype(predicate) => p!(print(predicate)), + ty::PredicateKind::RegionOutlives(predicate) => p!(print(predicate)), + ty::PredicateKind::TypeOutlives(predicate) => p!(print(predicate)), + ty::PredicateKind::Projection(predicate) => p!(print(predicate)), + ty::PredicateKind::WellFormed(arg) => p!(print(arg), " well-formed"), + ty::PredicateKind::ObjectSafe(trait_def_id) => { p!("the trait `", print_def_path(trait_def_id, &[]), "` is object-safe") } - ty::PredicateAtom::ClosureKind(closure_def_id, _closure_substs, kind) => { + ty::PredicateKind::ClosureKind(closure_def_id, _closure_substs, kind) => { p!("the closure `", print_value_path(closure_def_id, &[]), write("` implements the trait `{}`", kind)) } - ty::PredicateAtom::ConstEvaluatable(def, substs) => { + ty::PredicateKind::ConstEvaluatable(def, substs) => { p!("the constant `", print_value_path(def.did, substs), "` can be evaluated") } - ty::PredicateAtom::ConstEquate(c1, c2) => { + ty::PredicateKind::ConstEquate(c1, c2) => { p!("the constant `", print(c1), "` equals `", print(c2), "`") } - ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { + ty::PredicateKind::TypeWellFormedFromEnv(ty) => { p!("the type `", print(ty), "` is found in the environment") } } diff --git a/compiler/rustc_middle/src/ty/query/keys.rs b/compiler/rustc_middle/src/ty/query/keys.rs index a005990264..bfa1581aaa 100644 --- a/compiler/rustc_middle/src/ty/query/keys.rs +++ b/compiler/rustc_middle/src/ty/query/keys.rs @@ -127,6 +127,17 @@ impl Key for (DefId, DefId) { } } +impl Key for (ty::Instance<'tcx>, LocalDefId) { + type CacheSelector = DefaultCacheSelector; + + fn query_crate(&self) -> CrateNum { + self.0.query_crate() + } + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + self.0.default_span(tcx) + } +} + impl Key for (DefId, LocalDefId) { type CacheSelector = DefaultCacheSelector; diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index b269dd09b7..f580cb14dc 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -1,4 +1,4 @@ -use crate::dep_graph::{self, DepKind, DepNode, DepNodeParams}; +use crate::dep_graph; use crate::hir::exports::Export; use crate::hir::map; use crate::infer::canonical::{self, Canonical}; @@ -103,138 +103,6 @@ pub use self::profiling_support::{IntoSelfProfilingString, QueryKeyStringBuilder rustc_query_append! { [define_queries!][<'tcx>] } -/// The red/green evaluation system will try to mark a specific DepNode in the -/// dependency graph as green by recursively trying to mark the dependencies of -/// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` -/// where we don't know if it is red or green and we therefore actually have -/// to recompute its value in order to find out. Since the only piece of -/// information that we have at that point is the `DepNode` we are trying to -/// re-evaluate, we need some way to re-run a query from just that. This is what -/// `force_from_dep_node()` implements. -/// -/// In the general case, a `DepNode` consists of a `DepKind` and an opaque -/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint -/// is usually constructed by computing a stable hash of the query-key that the -/// `DepNode` corresponds to. Consequently, it is not in general possible to go -/// back from hash to query-key (since hash functions are not reversible). For -/// this reason `force_from_dep_node()` is expected to fail from time to time -/// because we just cannot find out, from the `DepNode` alone, what the -/// corresponding query-key is and therefore cannot re-run the query. -/// -/// The system deals with this case letting `try_mark_green` fail which forces -/// the root query to be re-evaluated. -/// -/// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. -/// Fortunately, we can use some contextual information that will allow us to -/// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we -/// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a -/// valid `DefPathHash`. Since we also always build a huge table that maps every -/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have -/// everything we need to re-run the query. -/// -/// Take the `mir_promoted` query as an example. Like many other queries, it -/// just has a single parameter: the `DefId` of the item it will compute the -/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` -/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` -/// is actually a `DefPathHash`, and can therefore just look up the corresponding -/// `DefId` in `tcx.def_path_hash_to_def_id`. -/// -/// When you implement a new query, it will likely have a corresponding new -/// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As -/// a rule of thumb, if your query takes a `DefId` or `LocalDefId` as sole parameter, -/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just -/// add it to the "We don't have enough information to reconstruct..." group in -/// the match below. -pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool { - // We must avoid ever having to call `force_from_dep_node()` for a - // `DepNode::codegen_unit`: - // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we - // would always end up having to evaluate the first caller of the - // `codegen_unit` query that *is* reconstructible. This might very well be - // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just - // to re-trigger calling the `codegen_unit` query with the right key. At - // that point we would already have re-done all the work we are trying to - // avoid doing in the first place. - // The solution is simple: Just explicitly call the `codegen_unit` query for - // each CGU, right after partitioning. This way `try_mark_green` will always - // hit the cache instead of having to go through `force_from_dep_node`. - // This assertion makes sure, we actually keep applying the solution above. - debug_assert!( - dep_node.kind != DepKind::codegen_unit, - "calling force_from_dep_node() on DepKind::codegen_unit" - ); - - if !dep_node.kind.can_reconstruct_query_key() { - return false; - } - - macro_rules! force_from_dep_node { - ($($(#[$attr:meta])* [$($modifiers:tt)*] $name:ident($K:ty),)*) => { - match dep_node.kind { - // These are inputs that are expected to be pre-allocated and that - // should therefore always be red or green already. - DepKind::CrateMetadata | - - // These are anonymous nodes. - DepKind::TraitSelect | - - // We don't have enough information to reconstruct the query key of - // these. - DepKind::CompileCodegenUnit | - - // Forcing this makes no sense. - DepKind::Null => { - bug!("force_from_dep_node: encountered {:?}", dep_node) - } - - $(DepKind::$name => { - debug_assert!(<$K as DepNodeParams>>::can_reconstruct_query_key()); - - if let Some(key) = <$K as DepNodeParams>>::recover(tcx, dep_node) { - force_query::, _>( - tcx, - key, - DUMMY_SP, - *dep_node - ); - return true; - } - })* - } - } - } - - rustc_dep_node_append! { [force_from_dep_node!][] } - - false -} - -pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) { - macro_rules! try_load_from_on_disk_cache { - ($($name:ident,)*) => { - match dep_node.kind { - $(DepKind::$name => { - if as DepNodeParams>>::can_reconstruct_query_key() { - debug_assert!(tcx.dep_graph - .node_color(dep_node) - .map(|c| c.is_green()) - .unwrap_or(false)); - - let key = as DepNodeParams>>::recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); - if queries::$name::cache_on_disk(tcx, &key, None) { - let _ = tcx.$name(key); - } - } - })* - - _ => (), - } - } - } - - rustc_cached_queries!(try_load_from_on_disk_cache!); -} - mod sealed { use super::{DefId, LocalDefId}; @@ -262,3 +130,19 @@ mod sealed { } use sealed::IntoQueryParam; + +impl TyCtxt<'tcx> { + pub fn def_kind(self, def_id: impl IntoQueryParam) -> DefKind { + let def_id = def_id.into_query_param(); + self.opt_def_kind(def_id) + .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id)) + } +} + +impl TyCtxtAt<'tcx> { + pub fn def_kind(self, def_id: impl IntoQueryParam) -> DefKind { + let def_id = def_id.into_query_param(); + self.opt_def_kind(def_id) + .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id)) + } +} diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs index 8a1165bbd6..cfe47004e0 100644 --- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -1,19 +1,23 @@ use crate::dep_graph::{DepNode, DepNodeIndex, SerializedDepNodeIndex}; use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use crate::mir::{self, interpret}; -use crate::ty::codec::{OpaqueEncoder, RefDecodable, TyDecoder, TyEncoder}; +use crate::ty::codec::{RefDecodable, TyDecoder, TyEncoder}; use crate::ty::context::TyCtxt; use crate::ty::{self, Ty}; use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder, FingerprintEncoder}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, OnceCell}; use rustc_data_structures::thin_vec::ThinVec; +use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Diagnostic; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE}; use rustc_hir::definitions::DefPathHash; use rustc_hir::definitions::Definitions; use rustc_index::vec::{Idx, IndexVec}; -use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; +use rustc_serialize::{ + opaque::{self, FileEncodeResult, FileEncoder}, + Decodable, Decoder, Encodable, Encoder, +}; use rustc_session::{CrateDisambiguator, Session}; use rustc_span::hygiene::{ ExpnDataDecodeMode, ExpnDataEncodeMode, ExpnId, HygieneDecodeContext, HygieneEncodeContext, @@ -28,8 +32,10 @@ use std::mem; const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE; -const TAG_VALID_SPAN: u8 = 0; -const TAG_INVALID_SPAN: u8 = 1; +// A normal span encoded with both location information and a `SyntaxContext` +const TAG_FULL_SPAN: u8 = 0; +// A partial span with no location information, encoded only with a `SyntaxContext` +const TAG_PARTIAL_SPAN: u8 = 1; const TAG_SYNTAX_CONTEXT: u8 = 0; const TAG_EXPN_DATA: u8 = 1; @@ -87,7 +93,7 @@ pub struct OnDiskCache<'sess> { // compilation session. This is used as an initial 'guess' when // we try to map a `DefPathHash` to its `DefId` in the current compilation // session. - foreign_def_path_hashes: FxHashMap, + foreign_def_path_hashes: UnhashMap, // The *next* compilation sessison's `foreign_def_path_hashes` - at // the end of our current compilation session, this will get written @@ -95,19 +101,19 @@ pub struct OnDiskCache<'sess> { // will become `foreign_def_path_hashes` of the next compilation session. // This stores any `DefPathHash` that we may need to map to a `DefId` // during the next compilation session. - latest_foreign_def_path_hashes: Lock>, + latest_foreign_def_path_hashes: Lock>, // Maps `DefPathHashes` to their corresponding `LocalDefId`s for all // local items in the current compilation session. This is only populated // when we are in incremental mode and have loaded a pre-existing cache // from disk, since this map is only used when deserializing a `DefPathHash` // from the incremental cache. - local_def_path_hash_to_def_id: FxHashMap, + local_def_path_hash_to_def_id: UnhashMap, // Caches all lookups of `DefPathHashes`, both for local and foreign // definitions. A definition from the previous compilation session // may no longer exist in the current compilation session, so // we use `Option` so that we can cache a lookup failure. - def_path_hash_to_def_id_cache: Lock>>, + def_path_hash_to_def_id_cache: Lock>>, } // This type is used only for serialization and deserialization. @@ -123,7 +129,7 @@ struct Footer { syntax_contexts: FxHashMap, // See `OnDiskCache.expn_data` expn_data: FxHashMap, - foreign_def_path_hashes: FxHashMap, + foreign_def_path_hashes: UnhashMap, } type EncodedQueryResultIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; @@ -160,8 +166,8 @@ crate struct RawDefId { pub index: u32, } -fn make_local_def_path_hash_map(definitions: &Definitions) -> FxHashMap { - FxHashMap::from_iter( +fn make_local_def_path_hash_map(definitions: &Definitions) -> UnhashMap { + UnhashMap::from_iter( definitions .def_path_table() .all_def_path_hashes_and_def_ids(LOCAL_CRATE) @@ -240,10 +246,11 @@ impl<'sess> OnDiskCache<'sess> { } } - pub fn serialize<'tcx, E>(&self, tcx: TyCtxt<'tcx>, encoder: &mut E) -> Result<(), E::Error> - where - E: OpaqueEncoder, - { + pub fn serialize<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + encoder: &mut FileEncoder, + ) -> FileEncodeResult { // Serializing the `DepGraph` should not modify it. tcx.dep_graph.with_ignore(|| { // Allocate `SourceFileIndex`es. @@ -297,14 +304,14 @@ impl<'sess> OnDiskCache<'sess> { // Encode query results. let mut query_result_index = EncodedQueryResultIndex::new(); - tcx.sess.time("encode_query_results", || { + tcx.sess.time("encode_query_results", || -> FileEncodeResult { let enc = &mut encoder; let qri = &mut query_result_index; macro_rules! encode_queries { ($($query:ident,)*) => { $( - encode_query_results::, _>( + encode_query_results::>( tcx, enc, qri @@ -323,15 +330,17 @@ impl<'sess> OnDiskCache<'sess> { .current_diagnostics .borrow() .iter() - .map(|(dep_node_index, diagnostics)| { - let pos = AbsoluteBytePos::new(encoder.position()); - // Let's make sure we get the expected type here. - let diagnostics: &EncodedDiagnostics = diagnostics; - let dep_node_index = SerializedDepNodeIndex::new(dep_node_index.index()); - encoder.encode_tagged(dep_node_index, diagnostics)?; - - Ok((dep_node_index, pos)) - }) + .map( + |(dep_node_index, diagnostics)| -> Result<_, ::Error> { + let pos = AbsoluteBytePos::new(encoder.position()); + // Let's make sure we get the expected type here. + let diagnostics: &EncodedDiagnostics = diagnostics; + let dep_node_index = SerializedDepNodeIndex::new(dep_node_index.index()); + encoder.encode_tagged(dep_node_index, diagnostics)?; + + Ok((dep_node_index, pos)) + }, + ) .collect::>()?; let interpret_alloc_index = { @@ -374,13 +383,13 @@ impl<'sess> OnDiskCache<'sess> { hygiene_encode_context.encode( &mut encoder, - |encoder, index, ctxt_data| { + |encoder, index, ctxt_data| -> FileEncodeResult { let pos = AbsoluteBytePos::new(encoder.position()); encoder.encode_tagged(TAG_SYNTAX_CONTEXT, ctxt_data)?; syntax_contexts.insert(index, pos); Ok(()) }, - |encoder, index, expn_data| { + |encoder, index, expn_data| -> FileEncodeResult { let pos = AbsoluteBytePos::new(encoder.position()); encoder.encode_tagged(TAG_EXPN_DATA, expn_data)?; expn_ids.insert(index, pos); @@ -409,7 +418,7 @@ impl<'sess> OnDiskCache<'sess> { // Encode the position of the footer as the last 8 bytes of the // file so we know where to look for it. - IntEncodedWithFixedSize(footer_pos).encode(encoder.encoder.opaque())?; + IntEncodedWithFixedSize(footer_pos).encode(encoder.encoder)?; // DO NOT WRITE ANYTHING TO THE ENCODER AFTER THIS POINT! The address // of the footer must be the last thing in the data stream. @@ -807,6 +816,15 @@ impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { crate::implement_ty_decoder!(CacheDecoder<'a, 'tcx>); +// This ensures that the `Decodable::decode` specialization for `Vec` is used +// when a `CacheDecoder` is passed to `Decodable::decode`. Unfortunately, we have to manually opt +// into specializations this way, given how `CacheDecoder` and the decoding traits currently work. +impl<'a, 'tcx> Decodable> for Vec { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result { + Decodable::decode(&mut d.opaque) + } +} + impl<'a, 'tcx> Decodable> for SyntaxContext { fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Result { let syntax_contexts = decoder.syntax_contexts; @@ -848,10 +866,11 @@ impl<'a, 'tcx> Decodable> for Span { fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Result { let tag: u8 = Decodable::decode(decoder)?; - if tag == TAG_INVALID_SPAN { - return Ok(DUMMY_SP); + if tag == TAG_PARTIAL_SPAN { + let ctxt = SyntaxContext::decode(decoder)?; + return Ok(DUMMY_SP.with_ctxt(ctxt)); } else { - debug_assert_eq!(tag, TAG_VALID_SPAN); + debug_assert_eq!(tag, TAG_FULL_SPAN); } let file_lo_index = SourceFileIndex::decode(decoder)?; @@ -954,17 +973,28 @@ impl<'a, 'tcx> Decodable> for &'tcx [Span] { //- ENCODING ------------------------------------------------------------------- +trait OpaqueEncoder: Encoder { + fn position(&self) -> usize; +} + +impl OpaqueEncoder for FileEncoder { + #[inline] + fn position(&self) -> usize { + FileEncoder::position(self) + } +} + /// An encoder that can write to the incremental compilation cache. struct CacheEncoder<'a, 'tcx, E: OpaqueEncoder> { tcx: TyCtxt<'tcx>, encoder: &'a mut E, type_shorthands: FxHashMap, usize>, - predicate_shorthands: FxHashMap, usize>, + predicate_shorthands: FxHashMap, usize>, interpret_allocs: FxIndexSet, source_map: CachingSourceMapView<'tcx>, file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>, hygiene_context: &'a HygieneEncodeContext, - latest_foreign_def_path_hashes: FxHashMap, + latest_foreign_def_path_hashes: UnhashMap, } impl<'a, 'tcx, E> CacheEncoder<'a, 'tcx, E> @@ -995,9 +1025,9 @@ where } } -impl<'a, 'tcx> FingerprintEncoder for CacheEncoder<'a, 'tcx, rustc_serialize::opaque::Encoder> { - fn encode_fingerprint(&mut self, f: &Fingerprint) -> opaque::EncodeResult { - f.encode_opaque(self.encoder) +impl<'a, 'tcx, E: OpaqueEncoder> FingerprintEncoder for CacheEncoder<'a, 'tcx, E> { + fn encode_fingerprint(&mut self, f: &Fingerprint) -> Result<(), E::Error> { + self.encoder.encode_fingerprint(f) } } @@ -1030,24 +1060,29 @@ where { fn encode(&self, s: &mut CacheEncoder<'a, 'tcx, E>) -> Result<(), E::Error> { if *self == DUMMY_SP { - return TAG_INVALID_SPAN.encode(s); + TAG_PARTIAL_SPAN.encode(s)?; + return SyntaxContext::root().encode(s); } let span_data = self.data(); - let (file_lo, line_lo, col_lo) = match s.source_map.byte_pos_to_line_and_col(span_data.lo) { - Some(pos) => pos, - None => return TAG_INVALID_SPAN.encode(s), + let pos = s.source_map.byte_pos_to_line_and_col(span_data.lo); + let partial_span = match &pos { + Some((file_lo, _, _)) => !file_lo.contains(span_data.hi), + None => true, }; - if !file_lo.contains(span_data.hi) { - return TAG_INVALID_SPAN.encode(s); + if partial_span { + TAG_PARTIAL_SPAN.encode(s)?; + return span_data.ctxt.encode(s); } + let (file_lo, line_lo, col_lo) = pos.unwrap(); + let len = span_data.hi - span_data.lo; let source_file_index = s.source_file_index(file_lo); - TAG_VALID_SPAN.encode(s)?; + TAG_FULL_SPAN.encode(s)?; source_file_index.encode(s)?; line_lo.encode(s)?; col_lo.encode(s)?; @@ -1063,12 +1098,12 @@ where const CLEAR_CROSS_CRATE: bool = false; fn position(&self) -> usize { - self.encoder.encoder_position() + self.encoder.position() } fn type_shorthands(&mut self) -> &mut FxHashMap, usize> { &mut self.type_shorthands } - fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { &mut self.predicate_shorthands } fn encode_alloc_id(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { @@ -1149,6 +1184,16 @@ where } } +// This ensures that the `Encodable::encode` specialization for byte slices +// is used when a `CacheEncoder` having an `opaque::FileEncoder` is passed to `Encodable::encode`. +// Unfortunately, we have to manually opt into specializations this way, given how `CacheEncoder` +// and the encoding traits currently work. +impl<'a, 'tcx> Encodable> for [u8] { + fn encode(&self, e: &mut CacheEncoder<'a, 'tcx, FileEncoder>) -> FileEncodeResult { + self.encode(e.encoder) + } +} + // An integer that will always encode to 8 bytes. struct IntEncodedWithFixedSize(u64); @@ -1156,8 +1201,8 @@ impl IntEncodedWithFixedSize { pub const ENCODED_SIZE: usize = 8; } -impl Encodable for IntEncodedWithFixedSize { - fn encode(&self, e: &mut opaque::Encoder) -> Result<(), !> { +impl Encodable for IntEncodedWithFixedSize { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { let start_pos = e.position(); for i in 0..IntEncodedWithFixedSize::ENCODED_SIZE { ((self.0 >> (i * 8)) as u8).encode(e)?; @@ -1185,15 +1230,14 @@ impl<'a> Decodable> for IntEncodedWithFixedSize { } } -fn encode_query_results<'a, 'tcx, Q, E>( +fn encode_query_results<'a, 'tcx, Q>( tcx: TyCtxt<'tcx>, - encoder: &mut CacheEncoder<'a, 'tcx, E>, + encoder: &mut CacheEncoder<'a, 'tcx, FileEncoder>, query_result_index: &mut EncodedQueryResultIndex, -) -> Result<(), E::Error> +) -> FileEncodeResult where Q: super::QueryDescription> + super::QueryAccessors>, - Q::Value: Encodable>, - E: 'a + OpaqueEncoder, + Q::Value: Encodable>, { let _timer = tcx .sess @@ -1210,7 +1254,7 @@ where // Record position of the cache entry. query_result_index - .push((dep_node, AbsoluteBytePos::new(encoder.encoder.opaque().position()))); + .push((dep_node, AbsoluteBytePos::new(encoder.encoder.position()))); // Encode the type check tables with the `SerializedDepNodeIndex` // as tag. diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 7a1ca6a6c2..0ca94a9f18 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -111,81 +111,24 @@ impl fmt::Debug for ty::FreeRegion { } } -impl fmt::Debug for ty::Variance { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match *self { - ty::Covariant => "+", - ty::Contravariant => "-", - ty::Invariant => "o", - ty::Bivariant => "*", - }) - } -} - impl fmt::Debug for ty::FnSig<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "({:?}; c_variadic: {})->{:?}", self.inputs(), self.c_variadic, self.output()) } } -impl fmt::Debug for ty::TyVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}t", self.index) - } -} - impl<'tcx> fmt::Debug for ty::ConstVid<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "_#{}c", self.index) } } -impl fmt::Debug for ty::IntVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}i", self.index) - } -} - -impl fmt::Debug for ty::FloatVid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "_#{}f", self.index) - } -} - impl fmt::Debug for ty::RegionVid { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "'_#{}r", self.index()) } } -impl fmt::Debug for ty::InferTy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::TyVar(ref v) => v.fmt(f), - ty::IntVar(ref v) => v.fmt(f), - ty::FloatVar(ref v) => v.fmt(f), - ty::FreshTy(v) => write!(f, "FreshTy({:?})", v), - ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), - ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v), - } - } -} - -impl fmt::Debug for ty::IntVarValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::IntType(ref v) => v.fmt(f), - ty::UintType(ref v) => v.fmt(f), - } - } -} - -impl fmt::Debug for ty::FloatVarValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - impl fmt::Debug for ty::TraitRef<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { with_no_trimmed_paths(|| fmt::Display::fmt(self, f)) @@ -231,37 +174,28 @@ impl fmt::Debug for ty::Predicate<'tcx> { impl fmt::Debug for ty::PredicateKind<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - ty::PredicateKind::ForAll(binder) => write!(f, "ForAll({:?})", binder), - ty::PredicateKind::Atom(atom) => write!(f, "{:?}", atom), - } - } -} - -impl fmt::Debug for ty::PredicateAtom<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::PredicateAtom::Trait(ref a, constness) => { + ty::PredicateKind::Trait(ref a, constness) => { if let hir::Constness::Const = constness { write!(f, "const ")?; } a.fmt(f) } - ty::PredicateAtom::Subtype(ref pair) => pair.fmt(f), - ty::PredicateAtom::RegionOutlives(ref pair) => pair.fmt(f), - ty::PredicateAtom::TypeOutlives(ref pair) => pair.fmt(f), - ty::PredicateAtom::Projection(ref pair) => pair.fmt(f), - ty::PredicateAtom::WellFormed(data) => write!(f, "WellFormed({:?})", data), - ty::PredicateAtom::ObjectSafe(trait_def_id) => { + ty::PredicateKind::Subtype(ref pair) => pair.fmt(f), + ty::PredicateKind::RegionOutlives(ref pair) => pair.fmt(f), + ty::PredicateKind::TypeOutlives(ref pair) => pair.fmt(f), + ty::PredicateKind::Projection(ref pair) => pair.fmt(f), + ty::PredicateKind::WellFormed(data) => write!(f, "WellFormed({:?})", data), + ty::PredicateKind::ObjectSafe(trait_def_id) => { write!(f, "ObjectSafe({:?})", trait_def_id) } - ty::PredicateAtom::ClosureKind(closure_def_id, closure_substs, kind) => { + ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind) } - ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { + ty::PredicateKind::ConstEvaluatable(def_id, substs) => { write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs) } - ty::PredicateAtom::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2), - ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { + ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2), + ty::PredicateKind::TypeWellFormedFromEnv(ty) => { write!(f, "TypeWellFormedFromEnv({:?})", ty) } } @@ -283,7 +217,7 @@ TrivialTypeFoldableAndLiftImpls! { u64, String, crate::middle::region::Scope, - ::rustc_ast::FloatTy, + crate::ty::FloatTy, ::rustc_ast::InlineAsmOptions, ::rustc_ast::InlineAsmTemplatePiece, ::rustc_ast::NodeId, @@ -485,46 +419,36 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> { type Lifted = ty::PredicateKind<'tcx>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { match self { - ty::PredicateKind::ForAll(binder) => tcx.lift(binder).map(ty::PredicateKind::ForAll), - ty::PredicateKind::Atom(atom) => tcx.lift(atom).map(ty::PredicateKind::Atom), - } - } -} - -impl<'a, 'tcx> Lift<'tcx> for ty::PredicateAtom<'a> { - type Lifted = ty::PredicateAtom<'tcx>; - fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { - match self { - ty::PredicateAtom::Trait(data, constness) => { - tcx.lift(data).map(|data| ty::PredicateAtom::Trait(data, constness)) + ty::PredicateKind::Trait(data, constness) => { + tcx.lift(data).map(|data| ty::PredicateKind::Trait(data, constness)) } - ty::PredicateAtom::Subtype(data) => tcx.lift(data).map(ty::PredicateAtom::Subtype), - ty::PredicateAtom::RegionOutlives(data) => { - tcx.lift(data).map(ty::PredicateAtom::RegionOutlives) + ty::PredicateKind::Subtype(data) => tcx.lift(data).map(ty::PredicateKind::Subtype), + ty::PredicateKind::RegionOutlives(data) => { + tcx.lift(data).map(ty::PredicateKind::RegionOutlives) } - ty::PredicateAtom::TypeOutlives(data) => { - tcx.lift(data).map(ty::PredicateAtom::TypeOutlives) + ty::PredicateKind::TypeOutlives(data) => { + tcx.lift(data).map(ty::PredicateKind::TypeOutlives) } - ty::PredicateAtom::Projection(data) => { - tcx.lift(data).map(ty::PredicateAtom::Projection) + ty::PredicateKind::Projection(data) => { + tcx.lift(data).map(ty::PredicateKind::Projection) } - ty::PredicateAtom::WellFormed(ty) => tcx.lift(ty).map(ty::PredicateAtom::WellFormed), - ty::PredicateAtom::ClosureKind(closure_def_id, closure_substs, kind) => { + ty::PredicateKind::WellFormed(ty) => tcx.lift(ty).map(ty::PredicateKind::WellFormed), + ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { tcx.lift(closure_substs).map(|closure_substs| { - ty::PredicateAtom::ClosureKind(closure_def_id, closure_substs, kind) + ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) }) } - ty::PredicateAtom::ObjectSafe(trait_def_id) => { - Some(ty::PredicateAtom::ObjectSafe(trait_def_id)) + ty::PredicateKind::ObjectSafe(trait_def_id) => { + Some(ty::PredicateKind::ObjectSafe(trait_def_id)) } - ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { - tcx.lift(substs).map(|substs| ty::PredicateAtom::ConstEvaluatable(def_id, substs)) + ty::PredicateKind::ConstEvaluatable(def_id, substs) => { + tcx.lift(substs).map(|substs| ty::PredicateKind::ConstEvaluatable(def_id, substs)) } - ty::PredicateAtom::ConstEquate(c1, c2) => { - tcx.lift((c1, c2)).map(|(c1, c2)| ty::PredicateAtom::ConstEquate(c1, c2)) + ty::PredicateKind::ConstEquate(c1, c2) => { + tcx.lift((c1, c2)).map(|(c1, c2)| ty::PredicateKind::ConstEquate(c1, c2)) } - ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { - tcx.lift(ty).map(ty::PredicateAtom::TypeWellFormedFromEnv) + ty::PredicateKind::TypeWellFormedFromEnv(ty) => { + tcx.lift(ty).map(ty::PredicateKind::TypeWellFormedFromEnv) } } } @@ -1036,12 +960,12 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Region<'tcx> { impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { fn super_fold_with>(self, folder: &mut F) -> Self { - let new = ty::PredicateKind::super_fold_with(self.inner.kind, folder); + let new = self.inner.kind.fold_with(folder); folder.tcx().reuse_or_mk_predicate(self, new) } fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow { - ty::PredicateKind::super_visit_with(&self.inner.kind, visitor) + self.inner.kind.visit_with(visitor) } fn visit_with>(&self, visitor: &mut V) -> ControlFlow { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 4ce76409c6..c1fa84dcb2 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2,17 +2,16 @@ #![allow(rustc::usage_of_ty_tykind)] -use self::InferTy::*; use self::TyKind::*; use crate::infer::canonical::Canonical; use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; +use crate::ty::InferTy::{self, *}; use crate::ty::{ self, AdtDef, DefIdTree, Discr, Ty, TyCtxt, TypeFlags, TypeFoldable, WithConstness, }; use crate::ty::{DelaySpanBugEmitted, List, ParamEnv, TyS}; use polonius_engine::Atom; -use rustc_ast as ast; use rustc_data_structures::captures::Captures; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -104,13 +103,13 @@ pub enum TyKind<'tcx> { Char, /// A primitive signed integer type. For example, `i32`. - Int(ast::IntTy), + Int(ty::IntTy), /// A primitive unsigned integer type. For example, `u32`. - Uint(ast::UintTy), + Uint(ty::UintTy), /// A primitive floating-point type. For example, `f64`. - Float(ast::FloatTy), + Float(ty::FloatTy), /// Algebraic data types (ADT). For example: structures, enumerations and unions. /// @@ -605,7 +604,7 @@ impl<'tcx> GeneratorSubsts<'tcx> { #[inline] pub fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'tcx>) -> Range { // FIXME requires optimized MIR - let num_variants = tcx.generator_layout(def_id).variant_fields.len(); + let num_variants = tcx.generator_layout(def_id).unwrap().variant_fields.len(); VariantIdx::new(0)..VariantIdx::new(num_variants) } @@ -666,7 +665,7 @@ impl<'tcx> GeneratorSubsts<'tcx> { def_id: DefId, tcx: TyCtxt<'tcx>, ) -> impl Iterator> + Captures<'tcx>> { - let layout = tcx.generator_layout(def_id); + let layout = tcx.generator_layout(def_id).unwrap(); layout.variant_fields.iter().map(move |variant| { variant.iter().map(move |field| layout.field_tys[*field].subst(tcx, self.substs)) }) @@ -955,7 +954,9 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> { /// erase, or otherwise "discharge" these bound vars, we change the /// type from `Binder` to just `T` (see /// e.g., `liberate_late_bound_regions`). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] +/// +/// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct Binder(T); impl Binder { @@ -1131,8 +1132,16 @@ impl<'tcx> ProjectionTy<'tcx> { /// For example, if this is a projection of `::Item`, /// then this function would return a `T: Iterator` trait reference. pub fn trait_ref(&self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { + // FIXME: This method probably shouldn't exist at all, since it's not + // clear what this method really intends to do. Be careful when + // using this method since the resulting TraitRef additionally + // contains the substs for the assoc_item, which strictly speaking + // is not correct let def_id = tcx.associated_item(self.item_def_id).container.id(); - ty::TraitRef { def_id, substs: self.substs.truncate_to(tcx, tcx.generics_of(def_id)) } + // Include substitutions for generic arguments of associated types + let assoc_item = tcx.associated_item(self.item_def_id); + let substs_assoc_item = self.substs.truncate_to(tcx, tcx.generics_of(assoc_item.def_id)); + ty::TraitRef { def_id, substs: substs_assoc_item } } pub fn self_ty(&self) -> Ty<'tcx> { @@ -1424,28 +1433,15 @@ pub struct EarlyBoundRegion { pub name: Symbol, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] -pub struct TyVid { - pub index: u32, -} - +/// A **`const`** **v**ariable **ID**. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] pub struct ConstVid<'tcx> { pub index: u32, pub phantom: PhantomData<&'tcx ()>, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] -pub struct IntVid { - pub index: u32, -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] -pub struct FloatVid { - pub index: u32, -} - rustc_index::newtype_index! { + /// A **region** (lifetime) **v**ariable **ID**. pub struct RegionVid { DEBUG_FORMAT = custom, } @@ -1457,21 +1453,6 @@ impl Atom for RegionVid { } } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable)] -pub enum InferTy { - TyVar(TyVid), - IntVar(IntVid), - FloatVar(FloatVid), - - /// A `FreshTy` is one that is generated as a replacement for an - /// unbound type variable. This is convenient for caching etc. See - /// `infer::freshen` for more details. - FreshTy(u32), - FreshIntTy(u32), - FreshFloatTy(u32), -} - rustc_index::newtype_index! { pub struct BoundVar { .. } } @@ -1824,7 +1805,7 @@ impl<'tcx> TyS<'tcx> { pub fn sequence_element_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { match self.kind() { Array(ty, _) | Slice(ty) => ty, - Str => tcx.mk_mach_uint(ast::UintTy::U8), + Str => tcx.mk_mach_uint(ty::UintTy::U8), _ => bug!("`sequence_element_type` called on non-sequence value: {}", self), } } @@ -1898,8 +1879,14 @@ impl<'tcx> TyS<'tcx> { pub fn is_scalar(&self) -> bool { matches!( self.kind(), - Bool | Char | Int(_) | Float(_) | Uint(_) | FnDef(..) | FnPtr(_) | RawPtr(_) - | Infer(IntVar(_) | FloatVar(_)) + Bool | Char + | Int(_) + | Float(_) + | Uint(_) + | FnDef(..) + | FnPtr(_) + | RawPtr(_) + | Infer(IntVar(_) | FloatVar(_)) ) } @@ -1964,7 +1951,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_ptr_sized_integral(&self) -> bool { - matches!(self.kind(), Int(ast::IntTy::Isize) | Uint(ast::UintTy::Usize)) + matches!(self.kind(), Int(ty::IntTy::Isize) | Uint(ty::UintTy::Usize)) } #[inline] @@ -2152,9 +2139,9 @@ impl<'tcx> TyS<'tcx> { pub fn to_opt_closure_kind(&self) -> Option { match self.kind() { Int(int_ty) => match int_ty { - ast::IntTy::I8 => Some(ty::ClosureKind::Fn), - ast::IntTy::I16 => Some(ty::ClosureKind::FnMut), - ast::IntTy::I32 => Some(ty::ClosureKind::FnOnce), + ty::IntTy::I8 => Some(ty::ClosureKind::Fn), + ty::IntTy::I16 => Some(ty::ClosureKind::FnMut), + ty::IntTy::I32 => Some(ty::ClosureKind::FnOnce), _ => bug!("cannot convert type `{:?}` to a closure kind", self), }, diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 86476dffc0..f4d7eac0ae 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -63,7 +63,7 @@ pub enum TraitSpecializationKind { AlwaysApplicable, } -#[derive(Default)] +#[derive(Default, Debug)] pub struct TraitImpls { blanket_impls: Vec, /// Impls indexed by their simplified self type, for fast lookup. diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index a64580336a..8edde8794e 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -34,7 +34,7 @@ impl<'tcx> fmt::Display for Discr<'tcx> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match *self.ty.kind() { ty::Int(ity) => { - let size = ty::tls::with(|tcx| Integer::from_attr(&tcx, SignedInt(ity)).size()); + let size = ty::tls::with(|tcx| Integer::from_int_ty(&tcx, ity).size()); let x = self.val; // sign extend the raw representation to be an i128 let x = size.sign_extend(x) as i128; @@ -59,8 +59,8 @@ fn unsigned_max(size: Size) -> u128 { fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) { let (int, signed) = match *ty.kind() { - Int(ity) => (Integer::from_attr(&tcx, SignedInt(ity)), true), - Uint(uty) => (Integer::from_attr(&tcx, UnsignedInt(uty)), false), + Int(ity) => (Integer::from_int_ty(&tcx, ity), true), + Uint(uty) => (Integer::from_uint_ty(&tcx, uty), false), _ => bug!("non integer discriminant"), }; (int.size(), signed) @@ -642,8 +642,8 @@ impl<'tcx> ty::TyS<'tcx> { } ty::Char => Some(std::char::MAX as u128), ty::Float(fty) => Some(match fty { - ast::FloatTy::F32 => rustc_apfloat::ieee::Single::INFINITY.to_bits(), - ast::FloatTy::F64 => rustc_apfloat::ieee::Double::INFINITY.to_bits(), + ty::FloatTy::F32 => rustc_apfloat::ieee::Single::INFINITY.to_bits(), + ty::FloatTy::F64 => rustc_apfloat::ieee::Double::INFINITY.to_bits(), }), _ => None, }; @@ -661,8 +661,8 @@ impl<'tcx> ty::TyS<'tcx> { } ty::Char => Some(0), ty::Float(fty) => Some(match fty { - ast::FloatTy::F32 => (-::rustc_apfloat::ieee::Single::INFINITY).to_bits(), - ast::FloatTy::F64 => (-::rustc_apfloat::ieee::Double::INFINITY).to_bits(), + ty::FloatTy::F32 => (-::rustc_apfloat::ieee::Single::INFINITY).to_bits(), + ty::FloatTy::F64 => (-::rustc_apfloat::ieee::Double::INFINITY).to_bits(), }), _ => None, }; diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs index 0903ef5089..791d5060fe 100644 --- a/compiler/rustc_middle/src/util/bug.rs +++ b/compiler/rustc_middle/src/util/bug.rs @@ -3,7 +3,7 @@ use crate::ty::{tls, TyCtxt}; use rustc_span::{MultiSpan, Span}; use std::fmt; -use std::panic::Location; +use std::panic::{panic_any, Location}; #[cold] #[inline(never)] @@ -21,6 +21,7 @@ pub fn span_bug_fmt>(span: S, args: fmt::Arguments<'_>) -> ! opt_span_bug_fmt(Some(span), args, Location::caller()); } +#[track_caller] fn opt_span_bug_fmt>( span: Option, args: fmt::Arguments<'_>, @@ -31,7 +32,7 @@ fn opt_span_bug_fmt>( match (tcx, span) { (Some(tcx), Some(span)) => tcx.sess.diagnostic().span_bug(span, &msg), (Some(tcx), None) => tcx.sess.diagnostic().bug(&msg), - (None, _) => panic!(msg), + (None, _) => panic_any(msg), } }); unreachable!(); diff --git a/compiler/rustc_mir/src/borrow_check/borrow_set.rs b/compiler/rustc_mir/src/borrow_check/borrow_set.rs index b4299fbc5a..288eda32e4 100644 --- a/compiler/rustc_mir/src/borrow_check/borrow_set.rs +++ b/compiler/rustc_mir/src/borrow_check/borrow_set.rs @@ -149,7 +149,7 @@ impl<'tcx> BorrowSet<'tcx> { } crate fn activations_at_location(&self, location: Location) -> &[BorrowIndex] { - self.activation_map.get(&location).map(|activations| &activations[..]).unwrap_or(&[]) + self.activation_map.get(&location).map_or(&[], |activations| &activations[..]) } crate fn len(&self) -> usize { diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index 1474c7abfa..cd16a88e5f 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -141,6 +141,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.add_moved_or_invoked_closure_note(location, used_place, &mut err); let mut is_loop_move = false; + let mut in_pattern = false; for move_site in &move_site_vec { let move_out = self.move_data.moves[(*move_site).moi]; @@ -151,95 +152,88 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let move_msg = if move_spans.for_closure() { " into closure" } else { "" }; + let loop_message = if location == move_out.source || move_site.traversed_back_edge { + ", in previous iteration of loop" + } else { + "" + }; + if location == move_out.source { - err.span_label( - span, - format!( - "value {}moved{} here, in previous iteration of loop", - partially_str, move_msg - ), - ); is_loop_move = true; - } else if move_site.traversed_back_edge { - err.span_label( - move_span, - format!( - "value {}moved{} here, in previous iteration of loop", - partially_str, move_msg - ), - ); - } else { - if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = - move_spans - { - let place_name = self - .describe_place(moved_place.as_ref()) - .map(|n| format!("`{}`", n)) - .unwrap_or_else(|| "value".to_owned()); - match kind { - FnSelfUseKind::FnOnceCall => { + } + + if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans { + let place_name = self + .describe_place(moved_place.as_ref()) + .map(|n| format!("`{}`", n)) + .unwrap_or_else(|| "value".to_owned()); + match kind { + FnSelfUseKind::FnOnceCall => { + err.span_label( + fn_call_span, + &format!( + "{} {}moved due to this call{}", + place_name, partially_str, loop_message + ), + ); + err.span_note( + var_span, + "this value implements `FnOnce`, which causes it to be moved when called", + ); + } + FnSelfUseKind::Operator { self_arg } => { + err.span_label( + fn_call_span, + &format!( + "{} {}moved due to usage in operator{}", + place_name, partially_str, loop_message + ), + ); + if self.fn_self_span_reported.insert(fn_span) { + err.span_note( + self_arg.span, + "calling this operator moves the left-hand side", + ); + } + } + FnSelfUseKind::Normal { self_arg, implicit_into_iter } => { + if implicit_into_iter { err.span_label( fn_call_span, &format!( - "{} {}moved due to this call", - place_name, partially_str + "{} {}moved due to this implicit call to `.into_iter()`{}", + place_name, partially_str, loop_message ), ); - err.span_note( - var_span, - "this value implements `FnOnce`, which causes it to be moved when called", - ); - } - FnSelfUseKind::Operator { self_arg } => { + } else { err.span_label( fn_call_span, &format!( - "{} {}moved due to usage in operator", - place_name, partially_str + "{} {}moved due to this method call{}", + place_name, partially_str, loop_message ), ); - if self.fn_self_span_reported.insert(fn_span) { - err.span_note( - self_arg.span, - "calling this operator moves the left-hand side", - ); - } } - FnSelfUseKind::Normal { self_arg, implicit_into_iter } => { - if implicit_into_iter { - err.span_label( - fn_call_span, - &format!( - "{} {}moved due to this implicit call to `.into_iter()`", - place_name, partially_str - ), - ); - } else { - err.span_label( - fn_call_span, - &format!( - "{} {}moved due to this method call", - place_name, partially_str - ), - ); - } - // Avoid pointing to the same function in multiple different - // error messages - if self.fn_self_span_reported.insert(self_arg.span) { - err.span_note( - self_arg.span, - &format!("this function consumes the receiver `self` by taking ownership of it, which moves {}", place_name) - ); - } + // Avoid pointing to the same function in multiple different + // error messages + if self.fn_self_span_reported.insert(self_arg.span) { + err.span_note( + self_arg.span, + &format!("this function takes ownership of the receiver `self`, which moves {}", place_name) + ); } - // Deref::deref takes &self, which cannot cause a move - FnSelfUseKind::DerefCoercion { .. } => unreachable!(), } - } else { - err.span_label( - move_span, - format!("value {}moved{} here", partially_str, move_msg), - ); + // Deref::deref takes &self, which cannot cause a move + FnSelfUseKind::DerefCoercion { .. } => unreachable!(), + } + } else { + err.span_label( + move_span, + format!("value {}moved{} here{}", partially_str, move_msg, loop_message), + ); + // If the move error occurs due to a loop, don't show + // another message for the same span + if loop_message.is_empty() { move_spans.var_span_label( &mut err, format!( @@ -250,6 +244,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } + if let UseSpans::PatUse(span) = move_spans { err.span_suggestion_verbose( span.shrink_to_lo(), @@ -262,6 +257,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "ref ".to_string(), Applicability::MachineApplicable, ); + in_pattern = true; } if let Some(DesugaringKind::ForLoop(_)) = move_span.desugaring_kind() { @@ -293,9 +289,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - let ty = - Place::ty_from(used_place.local, used_place.projection, self.body, self.infcx.tcx) - .ty; + let ty = used_place.ty(self.body, self.infcx.tcx).ty; let needs_note = match ty.kind() { ty::Closure(id, _) => { let tables = self.infcx.tcx.typeck(id.expect_local()); @@ -310,7 +304,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let place = &self.move_data.move_paths[mpi].place; let ty = place.ty(self.body, self.infcx.tcx).ty; - if is_loop_move { + // If we're in pattern, we do nothing in favor of the previous suggestion (#80913). + if is_loop_move & !in_pattern { if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() { // We have a `&mut` ref, we need to reborrow on each iteration (#62112). err.span_suggestion_verbose( @@ -732,8 +727,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> (String, String, String, String) { // Define a small closure that we can use to check if the type of a place // is a union. - let union_ty = |place_base, place_projection| { - let ty = Place::ty_from(place_base, place_projection, self.body, self.infcx.tcx).ty; + let union_ty = |place_base| { + // Need to use fn call syntax `PlaceRef::ty` to determine the type of `place_base`; + // using a type annotation in the closure argument instead leads to a lifetime error. + let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty; ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty) }; @@ -751,15 +748,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // field access to a union. If we find that, then we will keep the place of the // union being accessed and the field that was being accessed so we can check the // second borrowed place for the same union and a access to a different field. - let Place { local, projection } = first_borrowed_place; - - let mut cursor = projection.as_ref(); - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in first_borrowed_place.iter_projections().rev() { match elem { - ProjectionElem::Field(field, _) if union_ty(local, proj_base).is_some() => { - return Some((PlaceRef { local, projection: proj_base }, field)); + ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => { + return Some((place_base, field)); } _ => {} } @@ -769,23 +761,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .and_then(|(target_base, target_field)| { // With the place of a union and a field access into it, we traverse the second // borrowed place and look for a access to a different field of the same union. - let Place { local, ref projection } = second_borrowed_place; - - let mut cursor = &projection[..]; - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in second_borrowed_place.iter_projections().rev() { if let ProjectionElem::Field(field, _) = elem { - if let Some(union_ty) = union_ty(local, proj_base) { - if field != target_field - && local == target_base.local - && proj_base == target_base.projection - { + if let Some(union_ty) = union_ty(place_base) { + if field != target_field && place_base == target_base { return Some(( - self.describe_any_place(PlaceRef { - local, - projection: proj_base, - }), + self.describe_any_place(place_base), self.describe_any_place(first_borrowed_place.as_ref()), self.describe_any_place(second_borrowed_place.as_ref()), union_ty.to_string(), @@ -1342,21 +1323,30 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Applicability::MachineApplicable, ); - let msg = match category { + match category { ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => { - format!("{} is returned here", kind) + let msg = format!("{} is returned here", kind); + err.span_note(constraint_span, &msg); } ConstraintCategory::CallArgument => { fr_name.highlight_region_name(&mut err); - format!("function requires argument type to outlive `{}`", fr_name) + if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) { + err.note( + "async blocks are not executed immediately and must either take a \ + reference or ownership of outside variables they use", + ); + } else { + let msg = format!("function requires argument type to outlive `{}`", fr_name); + err.span_note(constraint_span, &msg); + } } _ => bug!( "report_escaping_closure_capture called with unexpected constraint \ category: `{:?}`", category ), - }; - err.span_note(constraint_span, &msg); + } + err } @@ -1628,20 +1618,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> { let tcx = self.infcx.tcx; - match place.projection { - [] => StorageDeadOrDrop::LocalStorageDead, - [proj_base @ .., elem] => { + match place.last_projection() { + None => StorageDeadOrDrop::LocalStorageDead, + Some((place_base, elem)) => { // FIXME(spastorino) make this iterate - let base_access = self.classify_drop_access_kind(PlaceRef { - local: place.local, - projection: proj_base, - }); + let base_access = self.classify_drop_access_kind(place_base); match elem { ProjectionElem::Deref => match base_access { StorageDeadOrDrop::LocalStorageDead | StorageDeadOrDrop::BoxedStorageDead => { assert!( - Place::ty_from(place.local, proj_base, self.body, tcx).ty.is_box(), + place_base.ty(self.body, tcx).ty.is_box(), "Drop of value behind a reference or raw pointer" ); StorageDeadOrDrop::BoxedStorageDead @@ -1649,7 +1636,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { StorageDeadOrDrop::Destructor(_) => base_access, }, ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { - let base_ty = Place::ty_from(place.local, proj_base, self.body, tcx).ty; + let base_ty = place_base.ty(self.body, tcx).ty; match base_ty.kind() { ty::Adt(def, _) if def.has_dtor(tcx) => { // Report the outermost adt with a destructor @@ -1664,7 +1651,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { _ => base_access, } } - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Index(_) => base_access, diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs index eccb616822..06e3f4b91f 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs @@ -5,7 +5,7 @@ use std::collections::VecDeque; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_index::vec::IndexVec; -use rustc_infer::infer::NLLRegionVariableOrigin; +use rustc_infer::infer::NllRegionVariableOrigin; use rustc_middle::mir::{ Body, CastKind, ConstraintCategory, FakeReadCause, Local, Location, Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind, @@ -75,7 +75,7 @@ impl BorrowExplanation { LaterUseKind::FakeLetRead => "stored here", LaterUseKind::Other => "used here", }; - if !borrow_span.map(|sp| sp.overlaps(var_or_use_span)).unwrap_or(false) { + if !borrow_span.map_or(false, |sp| sp.overlaps(var_or_use_span)) { err.span_label( var_or_use_span, format!("{}borrow later {}", borrow_desc, message), @@ -258,7 +258,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let (category, from_closure, span) = self.regioncx.best_blame_constraint( &self.body, borrow_region, - NLLRegionVariableOrigin::FreeRegion, + NllRegionVariableOrigin::FreeRegion, |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region), ); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 81571fd730..04ea3cbd8b 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -103,7 +103,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let did = did.expect_local(); let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); - if let Some((span, name)) = + if let Some((span, hir_place)) = self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) { diag.span_note( @@ -111,7 +111,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &format!( "closure cannot be invoked more than once because it moves the \ variable `{}` out of its environment", - name, + ty::place_to_string_for_capture(self.infcx.tcx, hir_place) ), ); return; @@ -127,7 +127,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let did = did.expect_local(); let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); - if let Some((span, name)) = + if let Some((span, hir_place)) = self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) { diag.span_note( @@ -135,7 +135,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &format!( "closure cannot be moved more than once as it is not `Copy` due to \ moving the variable `{}` out of its environment", - name + ty::place_to_string_for_capture(self.infcx.tcx, hir_place) ), ); } @@ -215,6 +215,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { PlaceRef { local, projection: [proj_base @ .., elem] } => { match elem { ProjectionElem::Deref => { + // FIXME(project-rfc_2229#36): print capture precisely here. let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { let var_index = field.index(); @@ -259,6 +260,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ProjectionElem::Field(field, _ty) => { autoderef = true; + // FIXME(project-rfc_2229#36): print capture precisely here. let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { let var_index = field.index(); @@ -338,8 +340,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.describe_field(PlaceRef { local, projection: proj_base }, field) } ProjectionElem::Downcast(_, variant_index) => { - let base_ty = - Place::ty_from(place.local, place.projection, self.body, self.infcx.tcx).ty; + let base_ty = place.ty(self.body, self.infcx.tcx).ty; self.describe_field_from_ty(&base_ty, field, Some(*variant_index)) } ProjectionElem::Field(_, field_type) => { @@ -473,7 +474,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // If we didn't find an overloaded deref or index, then assume it's a // built in deref and check the type of the base. - let base_ty = Place::ty_from(deref_base.local, deref_base.projection, self.body, tcx).ty; + let base_ty = deref_base.ty(self.body, tcx).ty; if base_ty.is_unsafe_ptr() { BorrowedContentSource::DerefRawPointer } else if base_ty.is_mutable_ptr() { @@ -954,7 +955,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, def_id: DefId, target_place: PlaceRef<'tcx>, - places: &Vec>, + places: &[Operand<'tcx>], ) -> Option<(Span, Option, Span)> { debug!( "closure_span: def_id={:?} target_place={:?} places={:?}", diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs index b1cebbd1f3..fb7694b7d8 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs @@ -302,7 +302,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .find_map(|p| self.is_upvar_field_projection(p)); let deref_base = match deref_target_place.projection.as_ref() { - &[ref proj_base @ .., ProjectionElem::Deref] => { + [proj_base @ .., ProjectionElem::Deref] => { PlaceRef { local: deref_target_place.local, projection: &proj_base } } _ => bug!("deref_target_place is not a deref projection"), @@ -345,7 +345,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; let upvar = &self.upvars[upvar_field.unwrap().index()]; - let upvar_hir_id = upvar.var_hir_id; + // FIXME(project-rfc-2229#8): Improve borrow-check diagnostics in case of precise + // capture. + let upvar_hir_id = upvar.place.get_root_variable(); let upvar_name = upvar.name; let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index e1af6fc07c..333ac0738d 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -1,9 +1,12 @@ use rustc_hir as hir; use rustc_hir::Node; use rustc_index::vec::Idx; -use rustc_middle::mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location}; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::{ + hir::place::PlaceBase, + mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location}, +}; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; @@ -61,12 +64,29 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); - item_msg = format!("`{}`", access_place_desc.unwrap()); - if self.is_upvar_field_projection(access_place.as_ref()).is_some() { - reason = ", as it is not declared as mutable".to_string(); + let imm_borrow_derefed = self.upvars[upvar_index.index()] + .place + .place + .deref_tys() + .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not))); + + // If the place is immutable then: + // + // - Either we deref a immutable ref to get to our final place. + // - We don't capture derefs of raw ptrs + // - Or the final place is immut because the root variable of the capture + // isn't marked mut and we should suggest that to the user. + if imm_borrow_derefed { + // If we deref an immutable ref then the suggestion here doesn't help. + return; } else { - let name = self.upvars[upvar_index.index()].name; - reason = format!(", as `{}` is not declared as mutable", name); + item_msg = format!("`{}`", access_place_desc.unwrap()); + if self.is_upvar_field_projection(access_place.as_ref()).is_some() { + reason = ", as it is not declared as mutable".to_string(); + } else { + let name = self.upvars[upvar_index.index()].name; + reason = format!(", as `{}` is not declared as mutable", name); + } } } @@ -241,6 +261,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { format!("mut {}", self.local_names[local].unwrap()), Applicability::MachineApplicable, ); + let tcx = self.infcx.tcx; + if let ty::Closure(id, _) = the_place_err.ty(self.body, tcx).ty.kind() { + self.show_mutating_upvar(tcx, id, the_place_err, &mut err); + } } // Also suggest adding mut for upvars @@ -252,9 +276,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); + let captured_place = &self.upvars[upvar_index.index()].place; + err.span_label(span, format!("cannot {ACT}", ACT = act)); - let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id; + let upvar_hir_id = captured_place.get_root_variable(); + if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) { if let hir::PatKind::Binding( hir::BindingAnnotation::Unannotated, @@ -271,6 +298,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ); } } + + let tcx = self.infcx.tcx; + if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind() + { + if let ty::Closure(id, _) = ty.kind() { + self.show_mutating_upvar(tcx, id, the_place_err, &mut err); + } + } } // complete hack to approximate old AST-borrowck @@ -463,6 +498,45 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err.buffer(&mut self.errors_buffer); } + // point to span of upvar making closure call require mutable borrow + fn show_mutating_upvar( + &self, + tcx: TyCtxt<'_>, + id: &hir::def_id::DefId, + the_place_err: PlaceRef<'tcx>, + err: &mut DiagnosticBuilder<'_>, + ) { + let id = id.expect_local(); + let tables = tcx.typeck(id); + let hir_id = tcx.hir().local_def_id_to_hir_id(id); + let (span, place) = &tables.closure_kind_origins()[hir_id]; + let reason = if let PlaceBase::Upvar(upvar_id) = place.base { + let upvar = ty::place_to_string_for_capture(tcx, place); + match tables.upvar_capture(upvar_id) { + ty::UpvarCapture::ByRef(ty::UpvarBorrow { + kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, + .. + }) => { + format!("mutable borrow of `{}`", upvar) + } + ty::UpvarCapture::ByValue(_) => { + format!("possible mutation of `{}`", upvar) + } + val => bug!("upvar `{}` borrowed, but not mutably: {:?}", upvar, val), + } + } else { + bug!("not an upvar") + }; + err.span_label( + *span, + format!( + "calling `{}` requires mutable binding due to {}", + self.describe_place(the_place_err).unwrap(), + reason + ), + ); + } + /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected. fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) { err.span_label(sp, format!("cannot {}", act)); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs index 78da43c31c..058986593a 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs @@ -3,7 +3,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_infer::infer::{ error_reporting::nice_region_error::NiceRegionError, - error_reporting::unexpected_hidden_region_diagnostic, NLLRegionVariableOrigin, + error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin, }; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; use rustc_middle::ty::subst::Subst; @@ -75,13 +75,13 @@ crate enum RegionErrorKind<'tcx> { /// The region element that erroneously must be outlived by `longer_fr`. error_element: RegionElement, /// The origin of the placeholder region. - fr_origin: NLLRegionVariableOrigin, + fr_origin: NllRegionVariableOrigin, }, /// Any other lifetime error. RegionError { /// The origin of the region. - fr_origin: NLLRegionVariableOrigin, + fr_origin: NllRegionVariableOrigin, /// The region that should outlive `shorter_fr`. longer_fr: RegionVid, /// The region that should be shorter, but we can't prove it. @@ -269,7 +269,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { pub(in crate::borrow_check) fn report_region_error( &mut self, fr: RegionVid, - fr_origin: NLLRegionVariableOrigin, + fr_origin: NllRegionVariableOrigin, outlived_fr: RegionVid, outlives_suggestion: &mut OutlivesSuggestionBuilder, ) { @@ -590,8 +590,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut found = false; for (bound, _) in bounds { - if let ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(_, r)) = - bound.skip_binders() + if let ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_, r)) = + bound.kind().skip_binder() { let r = r.subst(self.infcx.tcx, substs); if let ty::RegionKind::ReStatic = r { diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs index a850b85e9b..4abc623fc5 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs @@ -12,7 +12,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { tcx: TyCtxt<'tcx>, body: &Body<'tcx>, local_names: &IndexVec>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], fr: RegionVid, ) -> Option<(Option, Span)> { debug!("get_var_name_and_span_for_region(fr={:?})", fr); @@ -21,6 +21,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("get_var_name_and_span_for_region: attempting upvar"); self.get_upvar_index_for_region(tcx, fr) .map(|index| { + // FIXME(project-rfc-2229#8): Use place span for diagnostics let (name, span) = self.get_upvar_name_and_span_for_region(tcx, upvars, index); (Some(name), span) }) @@ -59,10 +60,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { crate fn get_upvar_name_and_span_for_region( &self, tcx: TyCtxt<'tcx>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], upvar_index: usize, ) -> (Symbol, Span) { - let upvar_hir_id = upvars[upvar_index].var_hir_id; + let upvar_hir_id = upvars[upvar_index].place.get_root_variable(); debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id); let upvar_name = tcx.hir().name(upvar_hir_id); diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index 44044d5553..5db52db70a 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -5,11 +5,10 @@ use rustc_data_structures::graph::dominators::Dominators; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{HirId, Node}; +use rustc_hir::Node; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::mir::{ traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, PlaceRef, VarDebugInfoContents, @@ -18,7 +17,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt}; +use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt}; use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; use rustc_span::{Span, Symbol, DUMMY_SP}; @@ -73,16 +72,14 @@ crate use region_infer::RegionInferenceContext; // FIXME(eddyb) perhaps move this somewhere more centrally. #[derive(Debug)] -crate struct Upvar { +crate struct Upvar<'tcx> { + // FIXME(project-rfc_2229#36): print capture precisely here. name: Symbol, - // FIXME(project-rfc-2229#8): This should use Place or something similar - var_hir_id: HirId, + place: CapturedPlace<'tcx>, /// If true, the capture is behind a reference. by_ref: bool, - - mutability: Mutability, } const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref]; @@ -161,26 +158,13 @@ fn do_mir_borrowck<'a, 'tcx>( let upvars: Vec<_> = tables .closure_min_captures_flattened(def.did.to_def_id()) .map(|captured_place| { - let var_hir_id = match captured_place.place.base { - HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, - _ => bug!("Expected upvar"), - }; + let var_hir_id = captured_place.get_root_variable(); let capture = captured_place.info.capture_kind; let by_ref = match capture { ty::UpvarCapture::ByValue(_) => false, ty::UpvarCapture::ByRef(..) => true, }; - let mut upvar = Upvar { - name: tcx.hir().name(var_hir_id), - var_hir_id, - by_ref, - mutability: Mutability::Not, - }; - let bm = *tables.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); - if bm == ty::BindByValue(hir::Mutability::Mut) { - upvar.mutability = Mutability::Mut; - } - upvar + Upvar { name: tcx.hir().name(var_hir_id), place: captured_place.clone(), by_ref } }) .collect(); @@ -549,7 +533,7 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> { dominators: Dominators, /// Information about upvars not necessarily preserved in types or MIR - upvars: Vec, + upvars: Vec>, /// Names of local (user) variables (extracted from `var_debug_info`). local_names: IndexVec>, @@ -1374,13 +1358,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) { let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| { - if !place.projection.is_empty() { - if let Some(field) = this.is_upvar_field_projection(place.as_ref()) { + // We have three possibilities here: + // a. We are modifying something through a mut-ref + // b. We are modifying something that is local to our parent + // c. Current body is a nested closure, and we are modifying path starting from + // a Place captured by our parent closure. + + // Handle (c), the path being modified is exactly the path captured by our parent + if let Some(field) = this.is_upvar_field_projection(place.as_ref()) { + this.used_mut_upvars.push(field); + return; + } + + for (place_ref, proj) in place.iter_projections().rev() { + // Handle (a) + if proj == ProjectionElem::Deref { + match place_ref.ty(this.body(), this.infcx.tcx).ty.kind() { + // We aren't modifying a variable directly + ty::Ref(_, _, hir::Mutability::Mut) => return, + + _ => {} + } + } + + // Handle (c) + if let Some(field) = this.is_upvar_field_projection(place_ref) { this.used_mut_upvars.push(field); + return; } - } else { - this.used_mut.insert(place.local); } + + // Handle(b) + this.used_mut.insert(place.local); }; // This relies on the current way that by-value @@ -1740,20 +1749,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.check_if_full_path_is_moved(location, desired_action, place_span, flow_state); - if let [base_proj @ .., ProjectionElem::Subslice { from, to, from_end: false }] = - place_span.0.projection + if let Some((place_base, ProjectionElem::Subslice { from, to, from_end: false })) = + place_span.0.last_projection() { - let place_ty = - Place::ty_from(place_span.0.local, base_proj, self.body(), self.infcx.tcx); + let place_ty = place_base.ty(self.body(), self.infcx.tcx); if let ty::Array(..) = place_ty.ty.kind() { - let array_place = PlaceRef { local: place_span.0.local, projection: base_proj }; self.check_if_subslice_element_is_moved( location, desired_action, - (array_place, place_span.1), + (place_base, place_span.1), maybe_uninits, - *from, - *to, + from, + to, ); return; } @@ -1825,10 +1832,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { debug!("check_if_assigned_path_is_moved place: {:?}", place); // None case => assigning to `x` does not require `x` be initialized. - let mut cursor = &*place.projection.as_ref(); - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in place.iter_projections().rev() { match elem { ProjectionElem::Index(_/*operand*/) | ProjectionElem::ConstantIndex { .. } | @@ -1843,10 +1847,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ProjectionElem::Deref => { self.check_if_full_path_is_moved( location, InitializationRequiringAction::Use, - (PlaceRef { - local: place.local, - projection: proj_base, - }, span), flow_state); + (place_base, span), flow_state); // (base initialized; no need to // recur further) break; @@ -1862,15 +1863,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // assigning to `P.f` requires `P` itself // be already initialized let tcx = self.infcx.tcx; - let base_ty = Place::ty_from(place.local, proj_base, self.body(), tcx).ty; + let base_ty = place_base.ty(self.body(), tcx).ty; match base_ty.kind() { ty::Adt(def, _) if def.has_dtor(tcx) => { self.check_if_path_or_subpath_is_moved( location, InitializationRequiringAction::Assignment, - (PlaceRef { - local: place.local, - projection: proj_base, - }, span), flow_state); + (place_base, span), flow_state); // (base initialized; no need to // recur further) @@ -1880,10 +1878,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Once `let s; s.x = V; read(s.x);`, // is allowed, remove this match arm. ty::Adt(..) | ty::Tuple(..) => { - check_parent_of_field(self, location, PlaceRef { - local: place.local, - projection: proj_base, - }, span, flow_state); + check_parent_of_field(self, location, place_base, span, flow_state); // rust-lang/rust#21232, #54499, #54986: during period where we reject // partial initialization, do not complain about unnecessary `mut` on @@ -1965,9 +1960,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // no move out from an earlier location) then this is an attempt at initialization // of the union - we should error in that case. let tcx = this.infcx.tcx; - if let ty::Adt(def, _) = - Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind() - { + if let ty::Adt(def, _) = base.ty(this.body(), tcx).ty.kind() { if def.is_union() { if this.move_data.path_map[mpi].iter().any(|moi| { this.move_data.moves[*moi].source.is_predecessor_of(location, this.body) @@ -2162,9 +2155,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place: PlaceRef<'tcx>, is_local_mutation_allowed: LocalMutationIsAllowed, ) -> Result, PlaceRef<'tcx>> { - match place { - PlaceRef { local, projection: [] } => { - let local = &self.body.local_decls[local]; + debug!("is_mutable: place={:?}, is_local...={:?}", place, is_local_mutation_allowed); + match place.last_projection() { + None => { + let local = &self.body.local_decls[place.local]; match local.mutability { Mutability::Not => match is_local_mutation_allowed { LocalMutationIsAllowed::Yes => Ok(RootPlace { @@ -2186,11 +2180,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }), } } - PlaceRef { local: _, projection: [proj_base @ .., elem] } => { + Some((place_base, elem)) => { match elem { ProjectionElem::Deref => { - let base_ty = - Place::ty_from(place.local, proj_base, self.body(), self.infcx.tcx).ty; + let base_ty = place_base.ty(self.body(), self.infcx.tcx).ty; // Check the kind of deref to decide match base_ty.kind() { @@ -2208,10 +2201,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { _ => LocalMutationIsAllowed::Yes, }; - self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - mode, - ) + self.is_mutable(place_base, mode) } } } @@ -2229,10 +2219,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } // `Box` owns its content, so mutable if its location is mutable - _ if base_ty.is_box() => self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - is_local_mutation_allowed, - ), + _ if base_ty.is_box() => { + self.is_mutable(place_base, is_local_mutation_allowed) + } // Deref should only be for reference, pointers or boxes _ => bug!("Deref of unexpected type: {:?}", base_ty), } @@ -2248,11 +2237,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(field) = upvar_field_projection { let upvar = &self.upvars[field.index()]; debug!( - "upvar.mutability={:?} local_mutation_is_allowed={:?} \ - place={:?}", - upvar, is_local_mutation_allowed, place + "is_mutable: upvar.mutability={:?} local_mutation_is_allowed={:?} \ + place={:?}, place_base={:?}", + upvar, is_local_mutation_allowed, place, place_base ); - match (upvar.mutability, is_local_mutation_allowed) { + match (upvar.place.mutability, is_local_mutation_allowed) { ( Mutability::Not, LocalMutationIsAllowed::No @@ -2286,10 +2275,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // }); // } // ``` - let _ = self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - is_local_mutation_allowed, - )?; + let _ = + self.is_mutable(place_base, is_local_mutation_allowed)?; Ok(RootPlace { place_local: place.local, place_projection: place.projection, @@ -2298,10 +2285,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } } else { - self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - is_local_mutation_allowed, - ) + self.is_mutable(place_base, is_local_mutation_allowed) } } } diff --git a/compiler/rustc_mir/src/borrow_check/nll.rs b/compiler/rustc_mir/src/borrow_check/nll.rs index 359c5f261a..a0265b20d1 100644 --- a/compiler/rustc_mir/src/borrow_check/nll.rs +++ b/compiler/rustc_mir/src/borrow_check/nll.rs @@ -165,7 +165,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], ) -> NllOutput<'tcx> { let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default()); diff --git a/compiler/rustc_mir/src/borrow_check/path_utils.rs b/compiler/rustc_mir/src/borrow_check/path_utils.rs index 934729553a..80de3b4e36 100644 --- a/compiler/rustc_mir/src/borrow_check/path_utils.rs +++ b/compiler/rustc_mir/src/borrow_check/path_utils.rs @@ -143,31 +143,29 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool { /// of a closure type. pub(crate) fn is_upvar_field_projection( tcx: TyCtxt<'tcx>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], place_ref: PlaceRef<'tcx>, body: &Body<'tcx>, ) -> Option { - let mut place_projection = place_ref.projection; + let mut place_ref = place_ref; let mut by_ref = false; - if let [proj_base @ .., ProjectionElem::Deref] = place_projection { - place_projection = proj_base; + if let Some((place_base, ProjectionElem::Deref)) = place_ref.last_projection() { + place_ref = place_base; by_ref = true; } - match place_projection { - [base @ .., ProjectionElem::Field(field, _ty)] => { - let base_ty = Place::ty_from(place_ref.local, base, body, tcx).ty; - + match place_ref.last_projection() { + Some((place_base, ProjectionElem::Field(field, _ty))) => { + let base_ty = place_base.ty(body, tcx).ty; if (base_ty.is_closure() || base_ty.is_generator()) && (!by_ref || upvars[field.index()].by_ref) { - Some(*field) + Some(field) } else { None } } - _ => None, } } diff --git a/compiler/rustc_mir/src/borrow_check/prefixes.rs b/compiler/rustc_mir/src/borrow_check/prefixes.rs index 6c5d42296f..bdf2becb71 100644 --- a/compiler/rustc_mir/src/borrow_check/prefixes.rs +++ b/compiler/rustc_mir/src/borrow_check/prefixes.rs @@ -10,7 +10,7 @@ use super::MirBorrowckCtxt; use rustc_hir as hir; -use rustc_middle::mir::{Body, Place, PlaceRef, ProjectionElem}; +use rustc_middle::mir::{Body, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, TyCtxt}; pub trait IsPrefixOf<'tcx> { @@ -67,24 +67,23 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { // downcasts here, but may return a base of a downcast). 'cursor: loop { - match &cursor { - PlaceRef { local: _, projection: [] } => { + match cursor.last_projection() { + None => { self.next = None; return Some(cursor); } - PlaceRef { local: _, projection: [proj_base @ .., elem] } => { + Some((cursor_base, elem)) => { match elem { ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { // FIXME: add union handling - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { - cursor = PlaceRef { local: cursor.local, projection: proj_base }; + cursor = cursor_base; continue 'cursor; } ProjectionElem::Deref => { @@ -92,7 +91,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { } } - assert_eq!(*elem, ProjectionElem::Deref); + assert_eq!(elem, ProjectionElem::Deref); match self.kind { PrefixSet::Shallow => { @@ -105,8 +104,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { PrefixSet::All => { // All prefixes: just blindly enqueue the base // of the projection. - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } PrefixSet::Supporting => { @@ -119,7 +117,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { // derefs, except we stop at the deref of a shared // reference. - let ty = Place::ty_from(cursor.local, proj_base, self.body, self.tcx).ty; + let ty = cursor_base.ty(self.body, self.tcx).ty; match ty.kind() { ty::RawPtr(_) | ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Not) => { // don't continue traversing over derefs of raw pointers or shared @@ -129,14 +127,12 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { } ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Mut) => { - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } ty::Adt(..) if ty.is_box() => { - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs b/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs index d6e48deb03..86d9db294b 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs @@ -5,7 +5,7 @@ use super::{OutlivesConstraint, RegionInferenceContext}; use crate::borrow_check::type_check::Locations; -use rustc_infer::infer::NLLRegionVariableOrigin; +use rustc_infer::infer::NllRegionVariableOrigin; use rustc_middle::ty::TyCtxt; use std::io::{self, Write}; @@ -20,7 +20,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { writeln!(out, "| Free Region Mapping")?; for region in self.regions() { - if let NLLRegionVariableOrigin::FreeRegion = self.definitions[region].origin { + if let NllRegionVariableOrigin::FreeRegion = self.definitions[region].origin { let classification = self.universal_regions.region_classification(region).unwrap(); let outlived_by = self.universal_region_relations.regions_outlived_by(region); writeln!( diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs index 9d45f6fd0d..bbd512fd36 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs @@ -9,7 +9,7 @@ use rustc_hir::def_id::DefId; use rustc_index::vec::IndexVec; use rustc_infer::infer::canonical::QueryOutlivesConstraint; use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound}; -use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin}; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::mir::{ Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint, @@ -143,9 +143,9 @@ pub(crate) struct AppliedMemberConstraint { pub(crate) struct RegionDefinition<'tcx> { /// What kind of variable is this -- a free region? existential - /// variable? etc. (See the `NLLRegionVariableOrigin` for more + /// variable? etc. (See the `NllRegionVariableOrigin` for more /// info.) - pub(in crate::borrow_check) origin: NLLRegionVariableOrigin, + pub(in crate::borrow_check) origin: NllRegionVariableOrigin, /// Which universe is this region variable defined in? This is /// most often `ty::UniverseIndex::ROOT`, but when we encounter @@ -451,7 +451,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let scc = self.constraint_sccs.scc(variable); match self.definitions[variable].origin { - NLLRegionVariableOrigin::FreeRegion => { + NllRegionVariableOrigin::FreeRegion => { // For each free, universally quantified region X: // Add all nodes in the CFG to liveness constraints @@ -462,7 +462,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_values.add_element(scc, variable); } - NLLRegionVariableOrigin::Placeholder(placeholder) => { + NllRegionVariableOrigin::Placeholder(placeholder) => { // Each placeholder region is only visible from // its universe `ui` and its extensions. So we // can't just add it into `scc` unless the @@ -480,8 +480,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - NLLRegionVariableOrigin::RootEmptyRegion - | NLLRegionVariableOrigin::Existential { .. } => { + NllRegionVariableOrigin::RootEmptyRegion + | NllRegionVariableOrigin::Existential { .. } => { // For existential, regions, nothing to do. } } @@ -1348,7 +1348,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) { for (fr, fr_definition) in self.definitions.iter_enumerated() { match fr_definition.origin { - NLLRegionVariableOrigin::FreeRegion => { + NllRegionVariableOrigin::FreeRegion => { // Go through each of the universal regions `fr` and check that // they did not grow too large, accumulating any requirements // for our caller into the `outlives_requirements` vector. @@ -1360,12 +1360,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); } - NLLRegionVariableOrigin::Placeholder(placeholder) => { + NllRegionVariableOrigin::Placeholder(placeholder) => { self.check_bound_universal_region(fr, placeholder, errors_buffer); } - NLLRegionVariableOrigin::RootEmptyRegion - | NLLRegionVariableOrigin::Existential { .. } => { + NllRegionVariableOrigin::RootEmptyRegion + | NllRegionVariableOrigin::Existential { .. } => { // nothing to check here } } @@ -1449,7 +1449,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer.push(RegionErrorKind::RegionError { longer_fr: *longer_fr, shorter_fr: *shorter_fr, - fr_origin: NLLRegionVariableOrigin::FreeRegion, + fr_origin: NllRegionVariableOrigin::FreeRegion, is_reported: true, }); } @@ -1459,16 +1459,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { // a more complete picture on how to separate this responsibility. for (fr, fr_definition) in self.definitions.iter_enumerated() { match fr_definition.origin { - NLLRegionVariableOrigin::FreeRegion => { + NllRegionVariableOrigin::FreeRegion => { // handled by polonius above } - NLLRegionVariableOrigin::Placeholder(placeholder) => { + NllRegionVariableOrigin::Placeholder(placeholder) => { self.check_bound_universal_region(fr, placeholder, errors_buffer); } - NLLRegionVariableOrigin::RootEmptyRegion - | NLLRegionVariableOrigin::Existential { .. } => { + NllRegionVariableOrigin::RootEmptyRegion + | NllRegionVariableOrigin::Existential { .. } => { // nothing to check here } } @@ -1516,7 +1516,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer.push(RegionErrorKind::RegionError { longer_fr, shorter_fr: representative, - fr_origin: NLLRegionVariableOrigin::FreeRegion, + fr_origin: NllRegionVariableOrigin::FreeRegion, is_reported: true, }); } @@ -1539,7 +1539,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer.push(RegionErrorKind::RegionError { longer_fr, shorter_fr, - fr_origin: NLLRegionVariableOrigin::FreeRegion, + fr_origin: NllRegionVariableOrigin::FreeRegion, is_reported: !error_reported, }); @@ -1597,7 +1597,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let blame_span_category = self.find_outlives_blame_span( body, longer_fr, - NLLRegionVariableOrigin::FreeRegion, + NllRegionVariableOrigin::FreeRegion, shorter_fr, ); @@ -1656,7 +1656,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { longer_fr, error_element, - fr_origin: NLLRegionVariableOrigin::Placeholder(placeholder), + fr_origin: NllRegionVariableOrigin::Placeholder(placeholder), }); } @@ -1732,7 +1732,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2); match self.definitions[r2].origin { - NLLRegionVariableOrigin::Placeholder(placeholder) => { + NllRegionVariableOrigin::Placeholder(placeholder) => { let universe1 = self.definitions[r1].universe; debug!( "cannot_name_value_of: universe1={:?} placeholder={:?}", @@ -1741,9 +1741,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { universe1.cannot_name(placeholder.universe) } - NLLRegionVariableOrigin::RootEmptyRegion - | NLLRegionVariableOrigin::FreeRegion - | NLLRegionVariableOrigin::Existential { .. } => false, + NllRegionVariableOrigin::RootEmptyRegion + | NllRegionVariableOrigin::FreeRegion + | NllRegionVariableOrigin::Existential { .. } => false, } } @@ -1771,7 +1771,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, body: &Body<'tcx>, fr1: RegionVid, - fr1_origin: NLLRegionVariableOrigin, + fr1_origin: NllRegionVariableOrigin, fr2: RegionVid, ) -> (ConstraintCategory, Span) { let (category, _, span) = self.best_blame_constraint(body, fr1, fr1_origin, |r| { @@ -1933,7 +1933,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { .definitions .iter_enumerated() .find_map(|(r, definition)| match definition.origin { - NLLRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r), + NllRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r), _ => None, }) .unwrap(), @@ -1965,7 +1965,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, body: &Body<'tcx>, from_region: RegionVid, - from_region_origin: NLLRegionVariableOrigin, + from_region_origin: NllRegionVariableOrigin, target_test: impl Fn(RegionVid) -> bool, ) -> (ConstraintCategory, bool, Span) { debug!( @@ -2059,11 +2059,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { // // and here we prefer to blame the source (the y = x statement). let blame_source = match from_region_origin { - NLLRegionVariableOrigin::FreeRegion - | NLLRegionVariableOrigin::Existential { from_forall: false } => true, - NLLRegionVariableOrigin::RootEmptyRegion - | NLLRegionVariableOrigin::Placeholder(_) - | NLLRegionVariableOrigin::Existential { from_forall: true } => false, + NllRegionVariableOrigin::FreeRegion + | NllRegionVariableOrigin::Existential { from_forall: false } => true, + NllRegionVariableOrigin::RootEmptyRegion + | NllRegionVariableOrigin::Placeholder(_) + | NllRegionVariableOrigin::Existential { from_forall: true } => false, }; let find_region = |i: &usize| { @@ -2144,8 +2144,8 @@ impl<'tcx> RegionDefinition<'tcx> { // `init_universal_regions`. let origin = match rv_origin { - RegionVariableOrigin::NLL(origin) => origin, - _ => NLLRegionVariableOrigin::Existential { from_forall: false }, + RegionVariableOrigin::Nll(origin) => origin, + _ => NllRegionVariableOrigin::Existential { from_forall: false }, }; Self { origin, universe, external_name: None } diff --git a/compiler/rustc_mir/src/borrow_check/renumber.rs b/compiler/rustc_mir/src/borrow_check/renumber.rs index e563e37adc..9377473bef 100644 --- a/compiler/rustc_mir/src/borrow_check/renumber.rs +++ b/compiler/rustc_mir/src/borrow_check/renumber.rs @@ -1,5 +1,5 @@ use rustc_index::vec::IndexVec; -use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin}; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_middle::mir::visit::{MutVisitor, TyContext}; use rustc_middle::mir::{Body, Location, PlaceElem, Promoted}; use rustc_middle::ty::subst::SubstsRef; @@ -15,7 +15,7 @@ pub fn renumber_mir<'tcx>( debug!("renumber_mir()"); debug!("renumber_mir: body.arg_count={:?}", body.arg_count); - let mut visitor = NLLVisitor { infcx }; + let mut visitor = NllVisitor { infcx }; for body in promoted.iter_mut() { visitor.visit_body(body); @@ -33,16 +33,16 @@ where debug!("renumber_regions(value={:?})", value); infcx.tcx.fold_regions(value, &mut false, |_region, _depth| { - let origin = NLLRegionVariableOrigin::Existential { from_forall: false }; + let origin = NllRegionVariableOrigin::Existential { from_forall: false }; infcx.next_nll_region_var(origin) }) } -struct NLLVisitor<'a, 'tcx> { +struct NllVisitor<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, } -impl<'a, 'tcx> NLLVisitor<'a, 'tcx> { +impl<'a, 'tcx> NllVisitor<'a, 'tcx> { fn renumber_regions(&mut self, value: T) -> T where T: TypeFoldable<'tcx>, @@ -51,7 +51,7 @@ impl<'a, 'tcx> NLLVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'tcx> { +impl<'a, 'tcx> MutVisitor<'tcx> for NllVisitor<'a, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } diff --git a/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs b/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs index b7d22fab3d..157959b115 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs @@ -39,10 +39,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { user_provided_sig = None; } else { let typeck_results = self.tcx().typeck(mir_def_id); - user_provided_sig = match typeck_results.user_provided_sigs.get(&mir_def_id.to_def_id()) - { - None => None, - Some(user_provided_poly_sig) => { + user_provided_sig = typeck_results.user_provided_sigs.get(&mir_def_id.to_def_id()).map( + |user_provided_poly_sig| { // Instantiate the canonicalized variables from // user-provided signature (e.g., the `_` in the code // above) with fresh variables. @@ -54,18 +52,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // Replace the bound items in the fn sig with fresh // variables, so that they represent the view from // "inside" the closure. - Some( - self.infcx - .replace_bound_vars_with_fresh_vars( - body.span, - LateBoundRegionConversionTime::FnCall, - poly_sig, - ) - .0, - ) - } - } - }; + self.infcx + .replace_bound_vars_with_fresh_vars( + body.span, + LateBoundRegionConversionTime::FnCall, + poly_sig, + ) + .0 + }, + ); + } debug!( "equate_inputs_and_outputs: normalized_input_tys = {:?}, local_decls = {:?}", diff --git a/compiler/rustc_mir/src/borrow_check/type_check/liveness/local_use_map.rs b/compiler/rustc_mir/src/borrow_check/type_check/liveness/local_use_map.rs index 995e3a60a0..7e8a33efe1 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/liveness/local_use_map.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/liveness/local_use_map.rs @@ -58,11 +58,7 @@ impl vll::LinkElem for Appearance { } impl LocalUseMap { - crate fn build( - live_locals: &Vec, - elements: &RegionValueElements, - body: &Body<'_>, - ) -> Self { + crate fn build(live_locals: &[Local], elements: &RegionValueElements, body: &Body<'_>) -> Self { let nones = IndexVec::from_elem_n(None, body.local_decls.len()); let mut local_use_map = LocalUseMap { first_def_at: nones.clone(), diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index 42cd050abc..3ba06bdd6e 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -16,7 +16,7 @@ use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{ - InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin, + InferCtxt, InferOk, LateBoundRegionConversionTime, NllRegionVariableOrigin, }; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; @@ -43,10 +43,6 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations} use crate::dataflow::impls::MaybeInitializedPlaces; use crate::dataflow::move_paths::MoveData; use crate::dataflow::ResultsCursor; -use crate::transform::{ - check_consts::ConstCx, - promote_consts::should_suggest_const_in_array_repeat_expressions_attribute, -}; use crate::borrow_check::{ borrow_set::BorrowSet, @@ -132,7 +128,7 @@ pub(crate) fn type_check<'mir, 'tcx>( flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, elements: &Rc, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], ) -> MirTypeckResults<'tcx> { let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let mut constraints = MirTypeckRegionConstraints { @@ -821,7 +817,7 @@ struct BorrowCheckContext<'a, 'tcx> { all_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - upvars: &'a [Upvar], + upvars: &'a [Upvar<'tcx>], } crate struct MirTypeckResults<'tcx> { @@ -876,7 +872,7 @@ impl MirTypeckRegionConstraints<'tcx> { match self.placeholder_index_to_region.get(placeholder_index) { Some(&v) => v, None => { - let origin = NLLRegionVariableOrigin::Placeholder(placeholder); + let origin = NllRegionVariableOrigin::Placeholder(placeholder); let region = infcx.next_nll_region_var_in_universe(origin, placeholder.universe); self.placeholder_index_to_region.push(region); region @@ -1014,7 +1010,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } self.prove_predicate( - ty::PredicateAtom::WellFormed(inferred_ty.into()).to_predicate(self.tcx()), + ty::PredicateKind::WellFormed(inferred_ty.into()).to_predicate(self.tcx()), Locations::All(span), ConstraintCategory::TypeAnnotation, ); @@ -1266,7 +1262,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { obligations.obligations.push(traits::Obligation::new( ObligationCause::dummy(), param_env, - ty::PredicateAtom::WellFormed(revealed_ty.into()).to_predicate(infcx.tcx), + ty::PredicateKind::WellFormed(revealed_ty.into()).to_predicate(infcx.tcx), )); obligations.add( infcx @@ -1611,7 +1607,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.check_call_dest(body, term, &sig, destination, term_location); self.prove_predicates( - sig.inputs_and_output.iter().map(|ty| ty::PredicateAtom::WellFormed(ty.into())), + sig.inputs_and_output.iter().map(|ty| ty::PredicateKind::WellFormed(ty.into())), term_location.to_locations(), ConstraintCategory::Boring, ); @@ -1855,8 +1851,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.assert_iscleanup(body, block_data, unwind, true); } } - TerminatorKind::InlineAsm { ref destination, .. } => { - if let &Some(target) = destination { + TerminatorKind::InlineAsm { destination, .. } => { + if let Some(target) = destination { self.assert_iscleanup(body, block_data, target, is_cleanup); } } @@ -1997,22 +1993,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let span = body.source_info(location).span; let ty = operand.ty(body, tcx); if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { - let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env); - // To determine if `const_in_array_repeat_expressions` feature gate should - // be mentioned, need to check if the rvalue is promotable. - let should_suggest = - should_suggest_const_in_array_repeat_expressions_attribute( - &ccx, operand, - ); - debug!("check_rvalue: should_suggest={:?}", should_suggest); - let def_id = body.source.def_id().expect_local(); self.infcx.report_selection_error( &traits::Obligation::new( ObligationCause::new( span, self.tcx().hir().local_def_id_to_hir_id(def_id), - traits::ObligationCauseCode::RepeatVec(should_suggest), + traits::ObligationCauseCode::RepeatVec, ), self.param_env, ty::Binder::bind(ty::TraitRef::new( @@ -2490,7 +2477,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { body, ); let category = if let Some(field) = field { - ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id) + let var_hir_id = self.borrowck_context.upvars[field.index()].place.get_root_variable(); + // FIXME(project-rfc-2229#8): Use Place for better diagnostics + ConstraintCategory::ClosureUpvar(var_hir_id) } else { ConstraintCategory::Boring }; @@ -2694,7 +2683,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { category: ConstraintCategory, ) { self.prove_predicates( - Some(ty::PredicateAtom::Trait( + Some(ty::PredicateKind::Trait( ty::TraitPredicate { trait_ref }, hir::Constness::NotConst, )), diff --git a/compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs b/compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs index 91b1a1fbd9..6665eb5ad5 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/relate_tys.rs @@ -1,5 +1,5 @@ use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; -use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin}; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::{self, Const, Ty}; @@ -64,7 +64,7 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> { fn next_existential_region_var(&mut self, from_forall: bool) -> ty::Region<'tcx> { if self.borrowck_context.is_some() { - let origin = NLLRegionVariableOrigin::Existential { from_forall }; + let origin = NllRegionVariableOrigin::Existential { from_forall }; self.infcx.next_nll_region_var(origin) } else { self.infcx.tcx.lifetimes.re_erased @@ -81,7 +81,7 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> { fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { self.infcx.next_nll_region_var_in_universe( - NLLRegionVariableOrigin::Existential { from_forall: false }, + NllRegionVariableOrigin::Existential { from_forall: false }, universe, ) } diff --git a/compiler/rustc_mir/src/borrow_check/universal_regions.rs b/compiler/rustc_mir/src/borrow_check/universal_regions.rs index c1a0d9856b..4b1acc1cd1 100644 --- a/compiler/rustc_mir/src/borrow_check/universal_regions.rs +++ b/compiler/rustc_mir/src/borrow_check/universal_regions.rs @@ -20,7 +20,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::{BodyOwnerKind, HirId}; use rustc_index::vec::{Idx, IndexVec}; -use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin}; +use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; @@ -393,7 +393,7 @@ struct UniversalRegionsBuilder<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, } -const FR: NLLRegionVariableOrigin = NLLRegionVariableOrigin::FreeRegion; +const FR: NllRegionVariableOrigin = NllRegionVariableOrigin::FreeRegion; impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { fn build(self) -> UniversalRegions<'tcx> { @@ -486,7 +486,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let root_empty = self .infcx - .next_nll_region_var(NLLRegionVariableOrigin::RootEmptyRegion) + .next_nll_region_var(NllRegionVariableOrigin::RootEmptyRegion) .to_region_vid(); UniversalRegions { @@ -647,7 +647,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { trait InferCtxtExt<'tcx> { fn replace_free_regions_with_nll_infer_vars( &self, - origin: NLLRegionVariableOrigin, + origin: NllRegionVariableOrigin, value: T, ) -> T where @@ -655,7 +655,7 @@ trait InferCtxtExt<'tcx> { fn replace_bound_regions_with_nll_infer_vars( &self, - origin: NLLRegionVariableOrigin, + origin: NllRegionVariableOrigin, all_outlive_scope: LocalDefId, value: ty::Binder, indices: &mut UniversalRegionIndices<'tcx>, @@ -673,7 +673,7 @@ trait InferCtxtExt<'tcx> { impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { fn replace_free_regions_with_nll_infer_vars( &self, - origin: NLLRegionVariableOrigin, + origin: NllRegionVariableOrigin, value: T, ) -> T where @@ -684,7 +684,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { fn replace_bound_regions_with_nll_infer_vars( &self, - origin: NLLRegionVariableOrigin, + origin: NllRegionVariableOrigin, all_outlive_scope: LocalDefId, value: ty::Binder, indices: &mut UniversalRegionIndices<'tcx>, @@ -788,13 +788,13 @@ fn for_each_late_bound_region_defined_on<'tcx>( fn_def_id: DefId, mut f: impl FnMut(ty::Region<'tcx>), ) { - if let Some(late_bounds) = tcx.is_late_bound_map(fn_def_id.expect_local()) { - for late_bound in late_bounds.iter() { - let hir_id = HirId { owner: fn_def_id.expect_local(), local_id: *late_bound }; + if let Some((owner, late_bounds)) = tcx.is_late_bound_map(fn_def_id.expect_local()) { + for &late_bound in late_bounds.iter() { + let hir_id = HirId { owner, local_id: late_bound }; let name = tcx.hir().name(hir_id); let region_def_id = tcx.hir().local_def_id(hir_id); let liberated_region = tcx.mk_region(ty::ReFree(ty::FreeRegion { - scope: fn_def_id, + scope: owner.to_def_id(), bound_region: ty::BoundRegionKind::BrNamed(region_def_id.to_def_id(), name), })); f(liberated_region); diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs index df163f6562..252f5e7ef2 100644 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -298,6 +298,8 @@ pub fn eval_to_allocation_raw_provider<'tcx>( tcx.def_span(def.did), key.param_env, CompileTimeInterpreter::new(tcx.sess.const_eval_limit()), + // Statics (and promoteds inside statics) may access other statics, because unlike consts + // they do not have to behave "as if" they were evaluated at runtime. MemoryExtra { can_access_statics: is_static }, ); @@ -305,83 +307,35 @@ pub fn eval_to_allocation_raw_provider<'tcx>( match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) { Err(error) => { let err = ConstEvalErr::new(&ecx, error, None); - // errors in statics are always emitted as fatal errors - if is_static { - // Ensure that if the above error was either `TooGeneric` or `Reported` - // an error must be reported. - let v = err.report_as_error( - ecx.tcx.at(ecx.cur_span()), - "could not evaluate static initializer", - ); - - // If this is `Reveal:All`, then we need to make sure an error is reported but if - // this is `Reveal::UserFacing`, then it's expected that we could get a - // `TooGeneric` error. When we fall back to `Reveal::All`, then it will either - // succeed or we'll report this error then. - if key.param_env.reveal() == Reveal::All { - tcx.sess.delay_span_bug( - err.span, - &format!("static eval failure did not emit an error: {:#?}", v), - ); - } - - Err(v) - } else if let Some(def) = def.as_local() { - // constant defined in this crate, we can figure out a lint level! - match tcx.def_kind(def.did.to_def_id()) { - // constants never produce a hard error at the definition site. Anything else is - // a backwards compatibility hazard (and will break old versions of winapi for - // sure) - // - // note that validation may still cause a hard error on this very same constant, - // because any code that existed before validation could not have failed - // validation thus preventing such a hard error from being a backwards - // compatibility hazard - DefKind::Const | DefKind::AssocConst => { - let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - Err(err.report_as_lint( - tcx.at(tcx.def_span(def.did)), - "any use of this value will cause an error", - hir_id, - Some(err.span), - )) - } - // promoting runtime code is only allowed to error if it references broken - // constants any other kind of error will be reported to the user as a - // deny-by-default lint - _ => { - if let Some(p) = cid.promoted { - let span = tcx.promoted_mir_opt_const_arg(def.to_global())[p].span; - if let err_inval!(ReferencedConstant) = err.error { - Err(err.report_as_error( - tcx.at(span), - "evaluation of constant expression failed", - )) - } else { - Err(err.report_as_lint( - tcx.at(span), - "reaching this expression at runtime will panic or abort", - tcx.hir().local_def_id_to_hir_id(def.did), - Some(err.span), - )) - } - // anything else (array lengths, enum initializers, constant patterns) are - // reported as hard errors - } else { - Err(err.report_as_error( - ecx.tcx.at(ecx.cur_span()), - "evaluation of constant value failed", - )) - } - } - } + // Some CTFE errors raise just a lint, not a hard error; see + // . + let emit_as_lint = if let Some(def) = def.as_local() { + // (Associated) consts only emit a lint, since they might be unused. + matches!(tcx.def_kind(def.did.to_def_id()), DefKind::Const | DefKind::AssocConst) } else { - // use of broken constant from other crate - Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant")) + // use of broken constant from other crate: always an error + false + }; + if emit_as_lint { + let hir_id = tcx.hir().local_def_id_to_hir_id(def.as_local().unwrap().did); + Err(err.report_as_lint( + tcx.at(tcx.def_span(def.did)), + "any use of this value will cause an error", + hir_id, + Some(err.span), + )) + } else { + let msg = if is_static { + "could not evaluate static initializer" + } else { + "evaluation of constant value failed" + }; + Err(err.report_as_error(ecx.tcx.at(ecx.cur_span()), msg)) } } Ok(mplace) => { - // Since evaluation had no errors, valiate the resulting constant: + // Since evaluation had no errors, validate the resulting constant. + // This is a separate `try` block to provide more targeted error reporting. let validation = try { let mut ref_tracking = RefTracking::new(mplace); let mut inner = false; @@ -399,7 +353,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( } }; if let Err(error) = validation { - // Validation failed, report an error + // Validation failed, report an error. This is always a hard error. let err = ConstEvalErr::new(&ecx, error, None); Err(err.struct_error( ecx.tcx, diff --git a/compiler/rustc_mir/src/const_eval/fn_queries.rs b/compiler/rustc_mir/src/const_eval/fn_queries.rs index aca822a05b..8c18dfcb8d 100644 --- a/compiler/rustc_mir/src/const_eval/fn_queries.rs +++ b/compiler/rustc_mir/src/const_eval/fn_queries.rs @@ -126,7 +126,7 @@ fn is_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { matches!( node, hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl { constness: hir::Constness::Const, .. }, + kind: hir::ItemKind::Impl(hir::Impl { constness: hir::Constness::Const, .. }), .. }) ) diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index 72912dd76f..49126cfec6 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -13,6 +13,7 @@ use rustc_middle::mir::AssertMessage; use rustc_session::Limit; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::{Align, Size}; +use rustc_target::spec::abi::Abi; use crate::interpret::{ self, compile_time_machine, AllocId, Allocation, Frame, ImmTy, InterpCx, InterpResult, Memory, @@ -200,9 +201,26 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, type MemoryExtra = MemoryExtra; + fn load_mir( + ecx: &InterpCx<'mir, 'tcx, Self>, + instance: ty::InstanceDef<'tcx>, + ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { + match instance { + ty::InstanceDef::Item(def) => { + if ecx.tcx.is_ctfe_mir_available(def.did) { + Ok(ecx.tcx.mir_for_ctfe_opt_const_arg(def)) + } else { + throw_unsup!(NoMirFor(def.did)) + } + } + _ => Ok(ecx.tcx.instance_mir(instance)), + } + } + fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, + _abi: Abi, args: &[OpTy<'tcx>], _ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, _unwind: Option, // unwinding is not supported in consts diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs index ab7fadac91..ee78ff00c9 100644 --- a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs +++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs @@ -518,14 +518,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { // Check if we are assigning into a field of a union, if so, lookup the place // of the union so it is marked as initialized again. - if let [proj_base @ .., ProjectionElem::Field(_, _)] = place.projection { - if let ty::Adt(def, _) = - Place::ty_from(place.local, proj_base, self.builder.body, self.builder.tcx) - .ty - .kind() - { + if let Some((place_base, ProjectionElem::Field(_, _))) = place.last_projection() { + if let ty::Adt(def, _) = place_base.ty(self.builder.body, self.builder.tcx).ty.kind() { if def.is_union() { - place = PlaceRef { local: place.local, projection: proj_base } + place = place_base; } } } diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs index 6d224bcc50..128d8cff95 100644 --- a/compiler/rustc_mir/src/interpret/cast.rs +++ b/compiler/rustc_mir/src/interpret/cast.rs @@ -2,13 +2,11 @@ use std::convert::TryFrom; use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::{Float, FloatConvert}; -use rustc_ast::FloatTy; -use rustc_attr as attr; use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::mir::CastKind; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; -use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut}; use rustc_span::symbol::sym; use rustc_target::abi::{Integer, LayoutOf, Variants}; @@ -203,8 +201,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { match *cast_ty.kind() { Int(_) | Uint(_) | RawPtr(_) => { let size = match *cast_ty.kind() { - Int(t) => Integer::from_attr(self, attr::IntType::SignedInt(t)).size(), - Uint(t) => Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(), + Int(t) => Integer::from_int_ty(self, t).size(), + Uint(t) => Integer::from_uint_ty(self, t).size(), RawPtr(_) => self.pointer_size(), _ => bug!(), }; @@ -235,7 +233,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { match *dest_ty.kind() { // float -> uint Uint(t) => { - let size = Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(); + let size = Integer::from_uint_ty(self, t).size(); // `to_u128` is a saturating cast, which is what we need // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). let v = f.to_u128(size.bits_usize()).value; @@ -244,7 +242,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // float -> int Int(t) => { - let size = Integer::from_attr(self, attr::IntType::SignedInt(t)).size(); + let size = Integer::from_int_ty(self, t).size(); // `to_i128` is a saturating cast, which is what we need // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). let v = f.to_i128(size.bits_usize()).value; diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index 3d955576f0..7e9594dd6b 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -370,7 +370,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn cur_span(&self) -> Span { - self.stack().last().map(|f| f.current_span()).unwrap_or(self.tcx.span) + self.stack().last().map_or(self.tcx.span, |f| f.current_span()) } #[inline(always)] @@ -477,16 +477,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(promoted) = promoted { return Ok(&self.tcx.promoted_mir_opt_const_arg(def)[promoted]); } - match instance { - ty::InstanceDef::Item(def) => { - if self.tcx.is_mir_available(def.did) { - Ok(self.tcx.optimized_mir_opt_const_arg(def)) - } else { - throw_unsup!(NoMirFor(def.did)) - } - } - _ => Ok(self.tcx.instance_mir(instance)), - } + M::load_mir(self, instance) } /// Call this on things you got out of the MIR (so it is as generic as the current diff --git a/compiler/rustc_mir/src/interpret/intrinsics.rs b/compiler/rustc_mir/src/interpret/intrinsics.rs index 474e1f8e57..f4309c9cd9 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics.rs @@ -7,7 +7,7 @@ use std::convert::TryFrom; use rustc_hir::def_id::DefId; use rustc_middle::mir::{ self, - interpret::{uabs, ConstValue, GlobalId, InterpResult, Scalar}, + interpret::{ConstValue, GlobalId, InterpResult, Scalar}, BinOp, }; use rustc_middle::ty; @@ -141,9 +141,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::min_align_of_val | sym::size_of_val => { - let place = self.deref_operand(args[0])?; + // Avoid `deref_operand` -- this is not a deref, the ptr does not have to be + // dereferencable! + let place = self.ref_to_mplace(self.read_immediate(args[0])?)?; let (size, align) = self - .size_and_align_of(place.meta, place.layout)? + .size_and_align_of_mplace(place)? .ok_or_else(|| err_unsup_format!("`extern type` does not have known layout"))?; let result = match intrinsic_name { @@ -322,6 +324,29 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = Scalar::from_uint(truncated_bits, layout.size); self.write_scalar(result, dest)?; } + sym::copy | sym::copy_nonoverlapping => { + let elem_ty = instance.substs.type_at(0); + let elem_layout = self.layout_of(elem_ty)?; + let count = self.read_scalar(args[2])?.to_machine_usize(self)?; + let elem_align = elem_layout.align.abi; + + let size = elem_layout.size.checked_mul(count, self).ok_or_else(|| { + err_ub_format!("overflow computing total size of `{}`", intrinsic_name) + })?; + let src = self.read_scalar(args[0])?.check_init()?; + let src = self.memory.check_ptr_access(src, size, elem_align)?; + let dest = self.read_scalar(args[1])?.check_init()?; + let dest = self.memory.check_ptr_access(dest, size, elem_align)?; + + if let (Some(src), Some(dest)) = (src, dest) { + self.memory.copy( + src, + dest, + size, + intrinsic_name == sym::copy_nonoverlapping, + )?; + } + } sym::offset => { let ptr = self.read_scalar(args[0])?.check_init()?; let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; @@ -517,7 +542,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // memory between these pointers must be accessible. Note that we do not require the // pointers to be properly aligned (unlike a read/write operation). let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr }; - let size: u64 = uabs(offset_bytes); + let size = offset_bytes.unsigned_abs(); // This call handles checking for integer/NULL pointers. self.memory.check_ptr_access_align( min_ptr, diff --git a/compiler/rustc_mir/src/interpret/machine.rs b/compiler/rustc_mir/src/interpret/machine.rs index f50cc6c16e..53ac62d435 100644 --- a/compiler/rustc_mir/src/interpret/machine.rs +++ b/compiler/rustc_mir/src/interpret/machine.rs @@ -10,6 +10,7 @@ use rustc_middle::mir; use rustc_middle::ty::{self, Ty}; use rustc_span::def_id::DefId; use rustc_target::abi::Size; +use rustc_target::spec::abi::Abi; use super::{ AllocId, Allocation, AllocationExtra, CheckInAllocMsg, Frame, ImmTy, InterpCx, InterpResult, @@ -131,6 +132,16 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Whether to enforce the validity invariant fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; + /// Entry point for obtaining the MIR of anything that should get evaluated. + /// So not just functions and shims, but also const/static initializers, anonymous + /// constants, ... + fn load_mir( + ecx: &InterpCx<'mir, 'tcx, Self>, + instance: ty::InstanceDef<'tcx>, + ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> { + Ok(ecx.tcx.instance_mir(instance)) + } + /// Entry point to all function calls. /// /// Returns either the mir to use for the call, or `None` if execution should @@ -144,6 +155,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, + abi: Abi, args: &[OpTy<'tcx, Self::PointerTag>], ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, unwind: Option, @@ -154,6 +166,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn call_extra_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, fn_val: Self::ExtraFnVal, + abi: Abi, args: &[OpTy<'tcx, Self::PointerTag>], ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, unwind: Option, @@ -405,6 +418,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { fn call_extra_fn( _ecx: &mut InterpCx<$mir, $tcx, Self>, fn_val: !, + _abi: Abi, _args: &[OpTy<$tcx>], _ret: Option<(PlaceTy<$tcx>, mir::BasicBlock)>, _unwind: Option, diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index d9437a312a..88236458a2 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -511,6 +511,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Constant(ref constant) => { let val = self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal); + // This can still fail: + // * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all + // checked yet. + // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail. self.const_to_op(val, layout)? } }; diff --git a/compiler/rustc_mir/src/interpret/operator.rs b/compiler/rustc_mir/src/interpret/operator.rs index fc266fa74b..f508165501 100644 --- a/compiler/rustc_mir/src/interpret/operator.rs +++ b/compiler/rustc_mir/src/interpret/operator.rs @@ -1,10 +1,9 @@ use std::convert::TryFrom; use rustc_apfloat::Float; -use rustc_ast::FloatTy; use rustc_middle::mir; use rustc_middle::mir::interpret::{InterpResult, Scalar}; -use rustc_middle::ty::{self, layout::TyAndLayout, Ty}; +use rustc_middle::ty::{self, layout::TyAndLayout, FloatTy, Ty}; use rustc_target::abi::LayoutOf; use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy}; diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index a003380dda..efde7fe694 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -153,6 +153,7 @@ impl MemPlace { } } + #[inline] pub fn offset( self, offset: Size, diff --git a/compiler/rustc_mir/src/interpret/step.rs b/compiler/rustc_mir/src/interpret/step.rs index 95738db1f5..fbc72ad8ad 100644 --- a/compiler/rustc_mir/src/interpret/step.rs +++ b/compiler/rustc_mir/src/interpret/step.rs @@ -264,10 +264,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { NullaryOp(mir::NullOp::SizeOf, ty) => { let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty); let layout = self.layout_of(ty)?; - assert!( - !layout.is_unsized(), - "SizeOf nullary MIR operator called for unsized type" - ); + if layout.is_unsized() { + // FIXME: This should be a span_bug (#80742) + self.tcx.sess.delay_span_bug( + self.frame().current_span(), + &format!("SizeOf nullary MIR operator called for unsized type {}", ty), + ); + throw_inval!(SizeOfUnsizedType(ty)); + } self.write_scalar(Scalar::from_machine_usize(layout.size.bytes(), self), dest)?; } diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index a2931325a2..575667f9a9 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -219,7 +219,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let instance = match fn_val { FnVal::Instance(instance) => instance, FnVal::Other(extra) => { - return M::call_extra_fn(self, extra, args, ret, unwind); + return M::call_extra_fn(self, extra, caller_abi, args, ret, unwind); } }; @@ -264,10 +264,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::Item(_) => { // We need MIR for this fn - let body = match M::find_mir_or_eval_fn(self, instance, args, ret, unwind)? { - Some(body) => body, - None => return Ok(()), - }; + let body = + match M::find_mir_or_eval_fn(self, instance, caller_abi, args, ret, unwind)? { + Some(body) => body, + None => return Ok(()), + }; self.push_stack_frame( instance, diff --git a/compiler/rustc_mir/src/interpret/util.rs b/compiler/rustc_mir/src/interpret/util.rs index c2165db278..89f34cd07a 100644 --- a/compiler/rustc_mir/src/interpret/util.rs +++ b/compiler/rustc_mir/src/interpret/util.rs @@ -47,8 +47,7 @@ where let index = index .try_into() .expect("more generic parameters than can fit into a `u32`"); - let is_used = - unused_params.contains(index).map(|unused| !unused).unwrap_or(true); + let is_used = unused_params.contains(index).map_or(true, |unused| !unused); // Only recurse when generic parameters in fns, closures and generators // are used and require substitution. match (is_used, subst.needs_subst()) { diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 57aec0953b..0b7492631c 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -153,7 +153,7 @@ impl RefTracking } /// Format a path -fn write_path(out: &mut String, path: &Vec) { +fn write_path(out: &mut String, path: &[PathElem]) { use self::PathElem::*; for elem in path.iter() { @@ -391,7 +391,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } // Make sure this is dereferenceable and all. let size_and_align = try_validation!( - self.ecx.size_and_align_of(place.meta, place.layout), + self.ecx.size_and_align_of_mplace(place), self.path, err_ub!(InvalidMeta(msg)) => { "invalid {} metadata: {}", kind, msg }, ); @@ -515,7 +515,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' Ok(true) } ty::Float(_) | ty::Int(_) | ty::Uint(_) => { - let value = self.ecx.read_scalar(value)?; + let value = try_validation!( + self.ecx.read_scalar(value), + self.path, + err_unsup!(ReadPointerAsBytes) => { "read of part of a pointer" }, + ); // NOTE: Keep this in sync with the array optimization for int/float // types below! if self.ctfe_mode.is_some() { diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs index e6d822086f..8b3881ef9d 100644 --- a/compiler/rustc_mir/src/lib.rs +++ b/compiler/rustc_mir/src/lib.rs @@ -57,6 +57,8 @@ pub fn provide(providers: &mut Providers) { providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; providers.const_caller_location = const_eval::const_caller_location; + providers.mir_callgraph_reachable = transform::inline::cycle::mir_callgraph_reachable; + providers.mir_inliner_callees = transform::inline::cycle::mir_inliner_callees; providers.destructure_const = |tcx, param_env_and_value| { let (param_env, value) = param_env_and_value.into_parts(); const_eval::destructure_const(tcx, param_env, value) diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index 6370ead97e..75f80f69be 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -823,7 +823,7 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> } if !tcx.is_mir_available(def_id) { - bug!("cannot create local mono-item for {:?}", def_id) + bug!("no MIR available for {:?}", def_id); } true @@ -1146,8 +1146,8 @@ fn create_mono_items_for_default_impls<'tcx>( output: &mut Vec>>, ) { match item.kind { - hir::ItemKind::Impl { ref generics, ref items, .. } => { - for param in generics.params { + hir::ItemKind::Impl(ref impl_) => { + for param in impl_.generics.params { match param.kind { hir::GenericParamKind::Lifetime { .. } => {} hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => { @@ -1167,7 +1167,7 @@ fn create_mono_items_for_default_impls<'tcx>( let param_env = ty::ParamEnv::reveal_all(); let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref); let overridden_methods: FxHashSet<_> = - items.iter().map(|iiref| iiref.ident.normalize_to_macros_2_0()).collect(); + impl_.items.iter().map(|iiref| iiref.ident.normalize_to_macros_2_0()).collect(); for method in tcx.provided_trait_methods(trait_ref.def_id) { if overridden_methods.contains(&method.ident.normalize_to_macros_2_0()) { continue; diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs index db6d3b2d91..b9fcd32250 100644 --- a/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs +++ b/compiler/rustc_mir/src/monomorphize/partitioning/mod.rs @@ -247,8 +247,7 @@ where for (mono_item, linkage) in cgu.items() { let symbol_name = mono_item.symbol_name(tcx).name; let symbol_hash_start = symbol_name.rfind('h'); - let symbol_hash = - symbol_hash_start.map(|i| &symbol_name[i..]).unwrap_or(""); + let symbol_hash = symbol_hash_start.map_or("", |i| &symbol_name[i..]); debug!( " - {} [{:?}] [{}] estimated size {}", diff --git a/compiler/rustc_mir/src/monomorphize/polymorphize.rs b/compiler/rustc_mir/src/monomorphize/polymorphize.rs index 0ce1c5a048..4ad71ab491 100644 --- a/compiler/rustc_mir/src/monomorphize/polymorphize.rs +++ b/compiler/rustc_mir/src/monomorphize/polymorphize.rs @@ -5,7 +5,7 @@ //! generic parameters are unused (and eventually, in what ways generic parameters are used - only //! for their size, offset of a field, etc.). -use rustc_hir::{def::DefKind, def_id::DefId}; +use rustc_hir::{def::DefKind, def_id::DefId, ConstContext}; use rustc_index::bit_set::FiniteBitSet; use rustc_middle::mir::{ visit::{TyContext, Visitor}, @@ -54,9 +54,17 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { } // Exit early when there is no MIR available. - if !tcx.is_mir_available(def_id) { - debug!("unused_generic_params: (no mir available) def_id={:?}", def_id); - return FiniteBitSet::new_empty(); + let context = tcx.hir().body_const_context(def_id.expect_local()); + match context { + Some(ConstContext::ConstFn) | None if !tcx.is_mir_available(def_id) => { + debug!("unused_generic_params: (no mir available) def_id={:?}", def_id); + return FiniteBitSet::new_empty(); + } + Some(_) if !tcx.is_ctfe_mir_available(def_id) => { + debug!("unused_generic_params: (no ctfe mir available) def_id={:?}", def_id); + return FiniteBitSet::new_empty(); + } + _ => {} } // Create a bitset with N rightmost ones for each parameter. @@ -69,7 +77,12 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { debug!("unused_generic_params: (after default) unused_parameters={:?}", unused_parameters); // Visit MIR and accumululate used generic parameters. - let body = tcx.optimized_mir(def_id); + let body = match context { + // Const functions are actually called and should thus be considered for polymorphization + // via their runtime MIR + Some(ConstContext::ConstFn) | None => tcx.optimized_mir(def_id), + Some(_) => tcx.mir_for_ctfe(def_id), + }; let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters }; vis.visit_body(body); debug!("unused_generic_params: (after visitor) unused_parameters={:?}", unused_parameters); diff --git a/compiler/rustc_mir/src/shim.rs b/compiler/rustc_mir/src/shim.rs index aa5835686a..6aaf27bdcb 100644 --- a/compiler/rustc_mir/src/shim.rs +++ b/compiler/rustc_mir/src/shim.rs @@ -81,7 +81,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' MirPhase::Const, &[&[ &add_moves_for_packed_drops::AddMovesForPackedDrops, - &no_landing_pads::NoLandingPads::new(tcx), + &no_landing_pads::NoLandingPads, &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::new("make_shim"), &add_call_guards::CriticalCallEdges, @@ -165,7 +165,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) let mut body = new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); - if let Some(..) = ty { + if ty.is_some() { // The first argument (index 0), but add 1 for the return value. let dropee_ptr = Place::from(Local::new(1 + 0)); if tcx.sess.opts.debugging_opts.mir_emit_retag { diff --git a/compiler/rustc_mir/src/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs index d2e65abfbc..6f98760a7b 100644 --- a/compiler/rustc_mir/src/transform/check_consts/ops.rs +++ b/compiler/rustc_mir/src/transform/check_consts/ops.rs @@ -72,7 +72,7 @@ impl NonConstOp for FnCallIndirect { /// A function call where the callee is not marked as `const`. #[derive(Debug)] -pub struct FnCallNonConst(pub DefId); +pub struct FnCallNonConst; impl NonConstOp for FnCallNonConst { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { struct_span_err!( @@ -209,30 +209,79 @@ impl NonConstOp for LiveDrop { } #[derive(Debug)] +/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to +/// the final value of the constant. +pub struct TransientCellBorrow; +impl NonConstOp for TransientCellBorrow { + fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + Status::Unstable(sym::const_refs_to_cell) + } + fn importance(&self) -> DiagnosticImportance { + // The cases that cannot possibly work will already emit a `CellBorrow`, so we should + // not additionally emit a feature gate error if activating the feature gate won't work. + DiagnosticImportance::Secondary + } + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + feature_err( + &ccx.tcx.sess.parse_sess, + sym::const_refs_to_cell, + span, + "cannot borrow here, since the borrowed element may contain interior mutability", + ) + } +} + +#[derive(Debug)] +/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to +/// the final value of the constant, and thus we cannot allow this (for now). We may allow +/// it in the future for static items. pub struct CellBorrow; impl NonConstOp for CellBorrow { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - struct_span_err!( + let mut err = struct_span_err!( ccx.tcx.sess, span, E0492, - "cannot borrow a constant which may contain \ - interior mutability, create a static instead" - ) + "{}s cannot refer to interior mutable data", + ccx.const_kind(), + ); + err.span_label( + span, + format!("this borrow of an interior mutable value may end up in the final value"), + ); + if let hir::ConstContext::Static(_) = ccx.const_kind() { + err.help( + "to fix this, the value can be extracted to a separate \ + `static` item and then referenced", + ); + } + if ccx.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "A constant containing interior mutable data behind a reference can allow you + to modify that data. This would make multiple uses of a constant to be able to + see different values and allow circumventing the `Send` and `Sync` requirements + for shared mutable data, which is unsound.", + ); + } + err } } #[derive(Debug)] +/// This op is for `&mut` borrows in the trailing expression of a constant +/// which uses the "enclosing scopes rule" to leak its locals into anonymous +/// static or const items. pub struct MutBorrow(pub hir::BorrowKind); impl NonConstOp for MutBorrow { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { - // Forbid everywhere except in const fn with a feature gate - if ccx.const_kind() == hir::ConstContext::ConstFn { - Status::Unstable(sym::const_mut_refs) - } else { - Status::Forbidden - } + fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { + Status::Forbidden + } + + fn importance(&self) -> DiagnosticImportance { + // If there were primary errors (like non-const function calls), do not emit further + // errors about mutable references. + DiagnosticImportance::Secondary } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { @@ -241,25 +290,15 @@ impl NonConstOp for MutBorrow { hir::BorrowKind::Ref => "", }; - let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn { - feature_err( - &ccx.tcx.sess.parse_sess, - sym::const_mut_refs, - span, - &format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()), - ) - } else { - let mut err = struct_span_err!( - ccx.tcx.sess, - span, - E0764, - "{}mutable references are not allowed in {}s", - raw, - ccx.const_kind(), - ); - err.span_label(span, format!("`&{}mut` is only allowed in `const fn`", raw)); - err - }; + let mut err = struct_span_err!( + ccx.tcx.sess, + span, + E0764, + "{}mutable references are not allowed in the final value of {}s", + raw, + ccx.const_kind(), + ); + if ccx.tcx.sess.teach(&err.get_code().unwrap()) { err.note( "References in statics and constants may only refer \ @@ -276,6 +315,29 @@ impl NonConstOp for MutBorrow { } } +#[derive(Debug)] +pub struct TransientMutBorrow(pub hir::BorrowKind); + +impl NonConstOp for TransientMutBorrow { + fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + Status::Unstable(sym::const_mut_refs) + } + + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + let raw = match self.0 { + hir::BorrowKind::Raw => "raw ", + hir::BorrowKind::Ref => "", + }; + + feature_err( + &ccx.tcx.sess.parse_sess, + sym::const_mut_refs, + span, + &format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()), + ) + } +} + #[derive(Debug)] pub struct MutDeref; impl NonConstOp for MutDeref { @@ -284,7 +346,7 @@ impl NonConstOp for MutDeref { } fn importance(&self) -> DiagnosticImportance { - // Usually a side-effect of a `MutBorrow` somewhere. + // Usually a side-effect of a `TransientMutBorrow` somewhere. DiagnosticImportance::Secondary } diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs index c66d3ed76d..0ce1980f10 100644 --- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs @@ -174,14 +174,10 @@ where Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { // Special-case reborrows to be more like a copy of the reference. - if let &[ref proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { - let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx).ty; + if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() { + let base_ty = place_base.ty(cx.body, cx.tcx).ty; if let ty::Ref(..) = base_ty.kind() { - return in_place::( - cx, - in_local, - PlaceRef { local: place.local, projection: proj_base }, - ); + return in_place::(cx, in_local, place_base); } } @@ -209,9 +205,9 @@ where Q: Qualif, F: FnMut(Local) -> bool, { - let mut projection = place.projection; - while let &[ref proj_base @ .., proj_elem] = projection { - match proj_elem { + let mut place = place; + while let Some((place_base, elem)) = place.last_projection() { + match elem { ProjectionElem::Index(index) if in_local(index) => return true, ProjectionElem::Deref @@ -222,16 +218,16 @@ where | ProjectionElem::Index(_) => {} } - let base_ty = Place::ty_from(place.local, proj_base, cx.body, cx.tcx); - let proj_ty = base_ty.projection_ty(cx.tcx, proj_elem).ty; + let base_ty = place_base.ty(cx.body, cx.tcx); + let proj_ty = base_ty.projection_ty(cx.tcx, elem).ty; if !Q::in_any_value_of_ty(cx, proj_ty) { return false; } - projection = proj_base; + place = place_base; } - assert!(projection.is_empty()); + assert!(place.projection.is_empty()); in_local(place.local) } diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index 90688ebbd0..a92997ddda 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -3,6 +3,7 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, HirId, LangItem}; +use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; @@ -188,6 +189,9 @@ pub struct Validator<'mir, 'tcx> { /// The span of the current statement. span: Span, + /// A set that stores for each local whether it has a `StorageDead` for it somewhere. + local_has_storage_dead: Option>, + error_emitted: Option, secondary_errors: Vec, } @@ -206,6 +210,7 @@ impl Validator<'mir, 'tcx> { span: ccx.body.span, ccx, qualifs: Default::default(), + local_has_storage_dead: None, error_emitted: None, secondary_errors: Vec::new(), } @@ -282,6 +287,27 @@ impl Validator<'mir, 'tcx> { } } + fn local_has_storage_dead(&mut self, local: Local) -> bool { + let ccx = self.ccx; + self.local_has_storage_dead + .get_or_insert_with(|| { + struct StorageDeads { + locals: BitSet, + } + impl Visitor<'tcx> for StorageDeads { + fn visit_statement(&mut self, stmt: &Statement<'tcx>, _: Location) { + if let StatementKind::StorageDead(l) = stmt.kind { + self.locals.insert(l); + } + } + } + let mut v = StorageDeads { locals: BitSet::new_empty(ccx.body.local_decls.len()) }; + v.visit_body(ccx.body); + v.locals + }) + .contains(local) + } + pub fn qualifs_in_return_place(&mut self) -> ConstQualifs { self.qualifs.in_return_place(self.ccx, self.error_emitted) } @@ -385,24 +411,24 @@ impl Validator<'mir, 'tcx> { loop { let predicates = tcx.predicates_of(current); for (predicate, _) in predicates.predicates { - match predicate.skip_binders() { - ty::PredicateAtom::RegionOutlives(_) - | ty::PredicateAtom::TypeOutlives(_) - | ty::PredicateAtom::WellFormed(_) - | ty::PredicateAtom::Projection(_) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue, - ty::PredicateAtom::ObjectSafe(_) => { + match predicate.kind().skip_binder() { + ty::PredicateKind::RegionOutlives(_) + | ty::PredicateKind::TypeOutlives(_) + | ty::PredicateKind::WellFormed(_) + | ty::PredicateKind::Projection(_) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, + ty::PredicateKind::ObjectSafe(_) => { bug!("object safe predicate on function: {:#?}", predicate) } - ty::PredicateAtom::ClosureKind(..) => { + ty::PredicateKind::ClosureKind(..) => { bug!("closure kind predicate on function: {:#?}", predicate) } - ty::PredicateAtom::Subtype(_) => { + ty::PredicateKind::Subtype(_) => { bug!("subtype predicate on function: {:#?}", predicate) } - ty::PredicateAtom::Trait(pred, constness) => { + ty::PredicateKind::Trait(pred, constness) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; } @@ -440,6 +466,29 @@ impl Validator<'mir, 'tcx> { } } } + + fn check_mut_borrow(&mut self, local: Local, kind: hir::BorrowKind) { + match self.const_kind() { + // In a const fn all borrows are transient or point to the places given via + // references in the arguments (so we already checked them with + // TransientMutBorrow/MutBorrow as appropriate). + // The borrow checker guarantees that no new non-transient borrows are created. + // NOTE: Once we have heap allocations during CTFE we need to figure out + // how to prevent `const fn` to create long-lived allocations that point + // to mutable memory. + hir::ConstContext::ConstFn => self.check_op(ops::TransientMutBorrow(kind)), + _ => { + // Locals with StorageDead do not live beyond the evaluation and can + // thus safely be borrowed without being able to be leaked to the final + // value of the constant. + if self.local_has_storage_dead(local) { + self.check_op(ops::TransientMutBorrow(kind)); + } else { + self.check_op(ops::MutBorrow(kind)); + } + } + } + } } impl Visitor<'tcx> for Validator<'mir, 'tcx> { @@ -536,15 +585,15 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { if !is_allowed { if let BorrowKind::Mut { .. } = kind { - self.check_op(ops::MutBorrow(hir::BorrowKind::Ref)); + self.check_mut_borrow(place.local, hir::BorrowKind::Ref) } else { self.check_op(ops::CellBorrow); } } } - Rvalue::AddressOf(Mutability::Mut, _) => { - self.check_op(ops::MutBorrow(hir::BorrowKind::Raw)) + Rvalue::AddressOf(Mutability::Mut, ref place) => { + self.check_mut_borrow(place.local, hir::BorrowKind::Raw) } Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place) @@ -556,7 +605,29 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { ); if borrowed_place_has_mut_interior { - self.check_op(ops::CellBorrow); + match self.const_kind() { + // In a const fn all borrows are transient or point to the places given via + // references in the arguments (so we already checked them with + // TransientCellBorrow/CellBorrow as appropriate). + // The borrow checker guarantees that no new non-transient borrows are created. + // NOTE: Once we have heap allocations during CTFE we need to figure out + // how to prevent `const fn` to create long-lived allocations that point + // to (interior) mutable memory. + hir::ConstContext::ConstFn => self.check_op(ops::TransientCellBorrow), + _ => { + // Locals with StorageDead are definitely not part of the final constant value, and + // it is thus inherently safe to permit such locals to have their + // address taken as we can't end up with a reference to them in the + // final value. + // Note: This is only sound if every local that has a `StorageDead` has a + // `StorageDead` in every control flow path leading to a `return` terminator. + if self.local_has_storage_dead(place.local) { + self.check_op(ops::TransientCellBorrow); + } else { + self.check_op(ops::CellBorrow); + } + } + } } } @@ -741,10 +812,10 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { } } + #[instrument(skip(self))] fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { use rustc_target::spec::abi::Abi::RustIntrinsic; - trace!("visit_terminator: terminator={:?} location={:?}", terminator, location); self.super_terminator(terminator, location); match &terminator.kind { @@ -768,8 +839,9 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { // Attempting to call a trait method? if let Some(trait_id) = tcx.trait_of_item(callee) { + trace!("attempting to call a trait method"); if !self.tcx.features().const_trait_impl { - self.check_op(ops::FnCallNonConst(callee)); + self.check_op(ops::FnCallNonConst); return; } @@ -823,25 +895,26 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { return; } + let is_intrinsic = tcx.fn_sig(callee).abi() == RustIntrinsic; + // HACK: This is to "unstabilize" the `transmute` intrinsic // within const fns. `transmute` is allowed in all other const contexts. // This won't really scale to more intrinsics or functions. Let's allow const // transmutes in const fn before we add more hacks to this. - if tcx.fn_sig(callee).abi() == RustIntrinsic - && tcx.item_name(callee) == sym::transmute - { + if is_intrinsic && tcx.item_name(callee) == sym::transmute { self.check_op(ops::Transmute); return; } if !tcx.is_const_fn_raw(callee) { - self.check_op(ops::FnCallNonConst(callee)); + self.check_op(ops::FnCallNonConst); return; } // If the `const fn` we are trying to call is not const-stable, ensure that we have // the proper feature gate enabled. if let Some(gate) = is_unstable_const_fn(tcx, callee) { + trace!(?gate, "calling unstable const fn"); if self.span.allows_unstable(gate) { return; } @@ -856,12 +929,14 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { // If this crate is not using stability attributes, or the caller is not claiming to be a // stable `const fn`, that is all that is required. if !self.ccx.is_const_stable_const_fn() { + trace!("crate not using stability attributes or caller not stably const"); return; } // Otherwise, we are something const-stable calling a const-unstable fn. if super::rustc_allow_const_fn_unstable(tcx, caller, gate) { + trace!("rustc_allow_const_fn_unstable gate active"); return; } @@ -875,10 +950,16 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { let callee_is_unstable_unmarked = tcx.lookup_const_stability(callee).is_none() && tcx.lookup_stability(callee).map_or(false, |s| s.level.is_unstable()); if callee_is_unstable_unmarked { - if self.ccx.is_const_stable_const_fn() { + trace!("callee_is_unstable_unmarked"); + // We do not use `const` modifiers for intrinsic "functions", as intrinsics are + // `extern` funtions, and these have no way to get marked `const`. So instead we + // use `rustc_const_(un)stable` attributes to mean that the intrinsic is `const` + if self.ccx.is_const_stable_const_fn() || is_intrinsic { self.check_op(ops::FnCallUnstable(callee, None)); + return; } } + trace!("permitting call"); } // Forbid all `Drop` terminators unless the place being dropped is a local with no @@ -959,27 +1040,26 @@ fn place_as_reborrow( body: &Body<'tcx>, place: Place<'tcx>, ) -> Option<&'a [PlaceElem<'tcx>]> { - place.projection.split_last().and_then(|(outermost, inner)| { - if outermost != &ProjectionElem::Deref { - return None; - } - - // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const` - // that points to the allocation for the static. Don't treat these as reborrows. - if body.local_decls[place.local].is_ref_to_static() { - return None; - } - - // Ensure the type being derefed is a reference and not a raw pointer. - // - // This is sufficient to prevent an access to a `static mut` from being marked as a - // reborrow, even if the check above were to disappear. - let inner_ty = Place::ty_from(place.local, inner, body, tcx).ty; - match inner_ty.kind() { - ty::Ref(..) => Some(inner), - _ => None, + match place.as_ref().last_projection() { + Some((place_base, ProjectionElem::Deref)) => { + // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const` + // that points to the allocation for the static. Don't treat these as reborrows. + if body.local_decls[place_base.local].is_ref_to_static() { + None + } else { + // Ensure the type being derefed is a reference and not a raw pointer. + // + // This is sufficient to prevent an access to a `static mut` from being marked as a + // reborrow, even if the check above were to disappear. + let inner_ty = place_base.ty(body, tcx).ty; + match inner_ty.kind() { + ty::Ref(..) => Some(place_base.projection), + _ => None, + } + } } - }) + _ => None, + } } fn is_int_bool_or_char(ty: Ty<'_>) -> bool { diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs index e64955c498..f0472758df 100644 --- a/compiler/rustc_mir/src/transform/check_unsafety.rs +++ b/compiler/rustc_mir/src/transform/check_unsafety.rs @@ -223,13 +223,6 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { // Check for raw pointer `Deref`. for (base, proj) in place.iter_projections() { if proj == ProjectionElem::Deref { - let source_info = self.source_info; // Backup source_info so we can restore it later. - if base.projection.is_empty() && decl.internal { - // Internal locals are used in the `move_val_init` desugaring. - // We want to check unsafety against the source info of the - // desugaring, rather than the source info of the RHS. - self.source_info = self.body.local_decls[place.local].source_info; - } let base_ty = base.ty(self.body, self.tcx).ty; if base_ty.is_unsafe_ptr() { self.require_unsafe( @@ -237,7 +230,6 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { UnsafetyViolationDetails::DerefOfRawPointer, ) } - self.source_info = source_info; // Restore backed-up source_info. } } @@ -407,17 +399,13 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { place: Place<'tcx>, is_mut_use: bool, ) { - let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in place.iter_projections().rev() { match elem { // Modifications behind a dereference don't affect the value of // the pointer. ProjectionElem::Deref => return, ProjectionElem::Field(..) => { - let ty = - Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty; + let ty = place_base.ty(&self.body.local_decls, self.tcx).ty; if let ty::Adt(def, _) = ty.kind() { if self.tcx.layout_scalar_valid_range(def.did) != (Bound::Unbounded, Bound::Unbounded) @@ -580,24 +568,23 @@ fn is_enclosed( tcx: TyCtxt<'_>, used_unsafe: &FxHashSet, id: hir::HirId, -) -> Option<(String, hir::HirId)> { + unsafe_op_in_unsafe_fn_allowed: bool, +) -> Option<(&'static str, hir::HirId)> { let parent_id = tcx.hir().get_parent_node(id); if parent_id != id { if used_unsafe.contains(&parent_id) { - Some(("block".to_string(), parent_id)) + Some(("block", parent_id)) } else if let Some(Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, _, _), .. })) = tcx.hir().find(parent_id) { - if sig.header.unsafety == hir::Unsafety::Unsafe - && !tcx.features().unsafe_block_in_unsafe_fn - { - Some(("fn".to_string(), parent_id)) + if sig.header.unsafety == hir::Unsafety::Unsafe && unsafe_op_in_unsafe_fn_allowed { + Some(("fn", parent_id)) } else { None } } else { - is_enclosed(tcx, used_unsafe, parent_id) + is_enclosed(tcx, used_unsafe, parent_id, unsafe_op_in_unsafe_fn_allowed) } } else { None @@ -610,7 +597,9 @@ fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet, id let msg = "unnecessary `unsafe` block"; let mut db = lint.build(msg); db.span_label(span, msg); - if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) { + if let Some((kind, id)) = + is_enclosed(tcx, used_unsafe, id, unsafe_op_in_unsafe_fn_allowed(tcx, id)) + { db.span_label( tcx.sess.source_map().guess_head_span(tcx.hir().span(id)), format!("because it's nested under this `unsafe` {}", kind), diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index 1d949e020e..fd5c223690 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -25,6 +25,7 @@ use rustc_middle::ty::{ use rustc_session::lint; use rustc_span::{def_id::DefId, Span}; use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TargetDataLayout}; +use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use crate::const_eval::ConstEvalErr; @@ -138,7 +139,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { Default::default(), body.arg_count, Default::default(), - tcx.def_span(def_id), + body.span, body.generator_kind, ); @@ -184,9 +185,17 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> type MemoryExtra = (); + fn load_mir( + _ecx: &InterpCx<'mir, 'tcx, Self>, + _instance: ty::InstanceDef<'tcx>, + ) -> InterpResult<'tcx, &'tcx Body<'tcx>> { + throw_machine_stop_str!("calling functions isn't supported in ConstProp") + } + fn find_mir_or_eval_fn( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _instance: ty::Instance<'tcx>, + _abi: Abi, _args: &[OpTy<'tcx>], _ret: Option<(PlaceTy<'tcx>, BasicBlock)>, _unwind: Option, @@ -431,7 +440,15 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } fn lint_root(&self, source_info: SourceInfo) -> Option { - match &self.source_scopes[source_info.scope].local_data { + let mut data = &self.source_scopes[source_info.scope]; + // FIXME(oli-obk): we should be able to just walk the `inlined_parent_scope`, but it + // does not work as I thought it would. Needs more investigation and documentation. + while data.inlined.is_some() { + trace!(?data); + data = &self.source_scopes[data.parent_scope.unwrap()]; + } + trace!(?data); + match &data.local_data { ClearCrossCrate::Set(data) => Some(data.lint_root), ClearCrossCrate::Clear => None, } diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs index 20f6a16e0f..272a7bf961 100644 --- a/compiler/rustc_mir/src/transform/coverage/counters.rs +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -32,7 +32,7 @@ impl CoverageCounters { } /// Activate the `DebugCounters` data structures, to provide additional debug formatting - /// features when formating `CoverageKind` (counter) values. + /// features when formatting `CoverageKind` (counter) values. pub fn enable_debug(&mut self) { self.debug_counters.enable(); } @@ -140,7 +140,7 @@ impl<'a> BcbCounters<'a> { /// message for subsequent debugging. fn make_bcb_counters( &mut self, - coverage_spans: &Vec, + coverage_spans: &[CoverageSpan], ) -> Result, Error> { debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); let num_bcbs = self.basic_coverage_blocks.num_nodes(); @@ -465,7 +465,7 @@ impl<'a> BcbCounters<'a> { fn choose_preferred_expression_branch( &self, traversal: &TraverseCoverageGraphWithLoops, - branches: &Vec, + branches: &[BcbBranch], ) -> BcbBranch { let branch_needs_a_counter = |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); @@ -509,7 +509,7 @@ impl<'a> BcbCounters<'a> { fn find_some_reloop_branch( &self, traversal: &TraverseCoverageGraphWithLoops, - branches: &Vec, + branches: &[BcbBranch], ) -> Option { let branch_needs_a_counter = |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index b66e37436a..2cd0dc6b1f 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -130,7 +130,7 @@ const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; pub(super) fn debug_options<'a>() -> &'a DebugOptions { static DEBUG_OPTIONS: SyncOnceCell = SyncOnceCell::new(); - &DEBUG_OPTIONS.get_or_init(|| DebugOptions::from_env()) + &DEBUG_OPTIONS.get_or_init(DebugOptions::from_env) } /// Parses and maintains coverage-specific debug options captured from the environment variable @@ -430,7 +430,7 @@ impl GraphvizData { { bcb_to_coverage_spans_with_counters .entry(bcb) - .or_insert_with(|| Vec::new()) + .or_insert_with(Vec::new) .push((coverage_span.clone(), counter_kind.clone())); } } @@ -456,7 +456,7 @@ impl GraphvizData { if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() { bcb_to_dependency_counters .entry(bcb) - .or_insert_with(|| Vec::new()) + .or_insert_with(Vec::new) .push(counter_kind.clone()); } } @@ -527,8 +527,8 @@ impl UsedExpressions { pub fn add_expression_operands(&mut self, expression: &CoverageKind) { if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() { if let CoverageKind::Expression { id, lhs, rhs, .. } = *expression { - used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id); - used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id); + used_expression_operands.entry(lhs).or_insert_with(Vec::new).push(id); + used_expression_operands.entry(rhs).or_insert_with(Vec::new).push(id); } } } diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs index b1a1bb957e..e58b915f12 100644 --- a/compiler/rustc_mir/src/transform/coverage/graph.rs +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -394,7 +394,7 @@ impl BasicCoverageBlockData { let operand = counter_kind.as_operand_id(); if let Some(replaced) = self .edge_from_bcbs - .get_or_insert_with(|| FxHashMap::default()) + .get_or_insert_with(FxHashMap::default) .insert(from_bcb, counter_kind) { Error::from_string(format!( diff --git a/compiler/rustc_mir/src/transform/coverage/query.rs b/compiler/rustc_mir/src/transform/coverage/query.rs index aa34ae70ef..4b455a6a1b 100644 --- a/compiler/rustc_mir/src/transform/coverage/query.rs +++ b/compiler/rustc_mir/src/transform/coverage/query.rs @@ -4,7 +4,7 @@ use rustc_middle::mir::coverage::*; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, Coverage, CoverageInfo, Location}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::DefId; /// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each @@ -112,7 +112,7 @@ impl Visitor<'_> for CoverageVisitor { } fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { - let mir_body = tcx.optimized_mir(def_id); + let mir_body = mir_body(tcx, def_id); let mut coverage_visitor = CoverageVisitor { // num_counters always has at least the `ZERO` counter. @@ -129,8 +129,7 @@ fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo } fn covered_file_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option { - let mir_body = tcx.optimized_mir(def_id); - for bb_data in mir_body.basic_blocks().iter() { + for bb_data in mir_body(tcx, def_id).basic_blocks().iter() { for statement in bb_data.statements.iter() { if let StatementKind::Coverage(box ref coverage) = statement.kind { if let Some(code_region) = coverage.code_region.as_ref() { @@ -142,9 +141,17 @@ fn covered_file_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option { None } +/// This function ensures we obtain the correct MIR for the given item irrespective of +/// whether that means const mir or runtime mir. For `const fn` this opts for runtime +/// mir. +fn mir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx mir::Body<'tcx> { + let id = ty::WithOptConstParam::unknown(def_id); + let def = ty::InstanceDef::Item(id); + tcx.instance_mir(def) +} + fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx CodeRegion> { - let mir_body: &'tcx mir::Body<'tcx> = tcx.optimized_mir(def_id); - mir_body + mir_body(tcx, def_id) .basic_blocks() .iter() .map(|data| { diff --git a/compiler/rustc_mir/src/transform/function_item_references.rs b/compiler/rustc_mir/src/transform/function_item_references.rs index d592580af9..8d02ac6d9b 100644 --- a/compiler/rustc_mir/src/transform/function_item_references.rs +++ b/compiler/rustc_mir/src/transform/function_item_references.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{ self, subst::{GenericArgKind, Subst, SubstsRef}, - PredicateAtom, Ty, TyCtxt, TyS, + PredicateKind, Ty, TyCtxt, TyS, }; use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES; use rustc_span::{symbol::sym, Span}; @@ -99,13 +99,13 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> { &self, def_id: DefId, substs_ref: SubstsRef<'tcx>, - args: &Vec>, + args: &[Operand<'tcx>], source_info: SourceInfo, ) { let param_env = self.tcx.param_env(def_id); let bounds = param_env.caller_bounds(); for bound in bounds { - if let Some(bound_ty) = self.is_pointer_trait(&bound.skip_binders()) { + if let Some(bound_ty) = self.is_pointer_trait(&bound.kind().skip_binder()) { // Get the argument types as they appear in the function signature. let arg_defs = self.tcx.fn_sig(def_id).skip_binder().inputs(); for (arg_num, arg_def) in arg_defs.iter().enumerate() { @@ -131,8 +131,8 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> { } /// If the given predicate is the trait `fmt::Pointer`, returns the bound parameter type. - fn is_pointer_trait(&self, bound: &PredicateAtom<'tcx>) -> Option> { - if let ty::PredicateAtom::Trait(predicate, _) = bound { + fn is_pointer_trait(&self, bound: &PredicateKind<'tcx>) -> Option> { + if let ty::PredicateKind::Trait(predicate, _) = bound { if self.tcx.is_diagnostic_item(sym::pointer_trait, predicate.def_id()) { Some(predicate.trait_ref.self_ty()) } else { @@ -162,7 +162,7 @@ impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> { .unwrap_or(None) } - fn nth_arg_span(&self, args: &Vec>, n: usize) -> Span { + fn nth_arg_span(&self, args: &[Operand<'tcx>], n: usize) -> Span { match &args[n] { Operand::Copy(place) | Operand::Move(place) => { self.body.local_decls[place.local].source_info.span diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 6e7575c1d7..1635a95f46 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -17,6 +17,8 @@ use crate::transform::MirPass; use std::iter; use std::ops::{Range, RangeFrom}; +crate mod cycle; + const INSTR_COST: usize = 5; const CALL_PENALTY: usize = 25; const LANDINGPAD_PENALTY: usize = 50; @@ -37,10 +39,24 @@ struct CallSite<'tcx> { impl<'tcx> MirPass<'tcx> for Inline { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + // If you change this optimization level, also change the level in + // `mir_drops_elaborated_and_const_checked` for the call to `mir_inliner_callees`. + // Otherwise you will get an ICE about stolen MIR. if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { return; } + if tcx.sess.opts.debugging_opts.instrument_coverage { + // Since `Inline` happens after `InstrumentCoverage`, the function-specific coverage + // counters can be invalidated, such as by merging coverage counter statements from + // a pre-inlined function into a different function. This kind of change is invalid, + // so inlining must be skipped. Note: This check is performed here so inlining can + // be disabled without preventing other optimizations (regardless of `mir_opt_level`). + return; + } + + let span = trace_span!("inline", body = %tcx.def_path_str(body.source.def_id())); + let _guard = span.enter(); if inline(tcx, body) { debug!("running simplify cfg on {:?}", body.source); CfgSimplifier::new(body).simplify(); @@ -81,8 +97,8 @@ struct Inliner<'tcx> { codegen_fn_attrs: &'tcx CodegenFnAttrs, /// Caller HirID. hir_id: hir::HirId, - /// Stack of inlined instances. - history: Vec>, + /// Stack of inlined Instances. + history: Vec>, /// Indicates that the caller body has been modified. changed: bool, } @@ -94,13 +110,28 @@ impl Inliner<'tcx> { None => continue, Some(it) => it, }; + let span = trace_span!("process_blocks", %callsite.callee, ?bb); + let _guard = span.enter(); + + trace!( + "checking for self recursion ({:?} vs body_source: {:?})", + callsite.callee.def_id(), + caller_body.source.def_id() + ); + if callsite.callee.def_id() == caller_body.source.def_id() { + debug!("Not inlining a function into itself"); + continue; + } - if !self.is_mir_available(&callsite.callee, caller_body) { + if !self.is_mir_available(callsite.callee, caller_body) { debug!("MIR unavailable {}", callsite.callee); continue; } + let span = trace_span!("instance_mir", %callsite.callee); + let instance_mir_guard = span.enter(); let callee_body = self.tcx.instance_mir(callsite.callee.def); + drop(instance_mir_guard); if !self.should_inline(callsite, callee_body) { continue; } @@ -128,28 +159,61 @@ impl Inliner<'tcx> { } } - fn is_mir_available(&self, callee: &Instance<'tcx>, caller_body: &Body<'tcx>) -> bool { - if let InstanceDef::Item(_) = callee.def { - if !self.tcx.is_mir_available(callee.def_id()) { - return false; + #[instrument(skip(self, caller_body))] + fn is_mir_available(&self, callee: Instance<'tcx>, caller_body: &Body<'tcx>) -> bool { + match callee.def { + InstanceDef::Item(_) => { + // If there is no MIR available (either because it was not in metadata or + // because it has no MIR because it's an extern function), then the inliner + // won't cause cycles on this. + if !self.tcx.is_mir_available(callee.def_id()) { + return false; + } } + // These have no own callable MIR. + InstanceDef::Intrinsic(_) | InstanceDef::Virtual(..) => return false, + // This cannot result in an immediate cycle since the callee MIR is a shim, which does + // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we + // do not need to catch this here, we can wait until the inliner decides to continue + // inlining a second time. + InstanceDef::VtableShim(_) + | InstanceDef::ReifyShim(_) + | InstanceDef::FnPtrShim(..) + | InstanceDef::ClosureOnceShim { .. } + | InstanceDef::DropGlue(..) + | InstanceDef::CloneShim(..) => return true, + } + + if self.tcx.is_constructor(callee.def_id()) { + trace!("constructors always have MIR"); + // Constructor functions cannot cause a query cycle. + return true; } if let Some(callee_def_id) = callee.def_id().as_local() { let callee_hir_id = self.tcx.hir().local_def_id_to_hir_id(callee_def_id); - // Avoid a cycle here by only using `instance_mir` only if we have - // a lower `HirId` than the callee. This ensures that the callee will - // not inline us. This trick only works without incremental compilation. - // So don't do it if that is enabled. Also avoid inlining into generators, + // Avoid inlining into generators, // since their `optimized_mir` is used for layout computation, which can // create a cycle, even when no attempt is made to inline the function // in the other direction. - !self.tcx.dep_graph.is_fully_enabled() + caller_body.generator_kind.is_none() + && ( + // Avoid a cycle here by only using `instance_mir` only if we have + // a lower `HirId` than the callee. This ensures that the callee will + // not inline us. This trick only works without incremental compilation. + // So don't do it if that is enabled. + !self.tcx.dep_graph.is_fully_enabled() && self.hir_id < callee_hir_id - && caller_body.generator_kind.is_none() + // If we know for sure that the function we're calling will itself try to + // call us, then we avoid inlining that function. + || !self.tcx.mir_callgraph_reachable((callee, caller_body.source.def_id().expect_local())) + ) } else { - // This cannot result in a cycle since the callee MIR is from another crate - // and is already optimized. + // This cannot result in an immediate cycle since the callee MIR is from another crate + // and is already optimized. Any subsequent inlining may cause cycles, but we do + // not need to catch this here, we can wait until the inliner decides to continue + // inlining a second time. + trace!("functions from other crates always have MIR"); true } } @@ -194,8 +258,8 @@ impl Inliner<'tcx> { None } + #[instrument(skip(self, callee_body))] fn should_inline(&self, callsite: CallSite<'tcx>, callee_body: &Body<'tcx>) -> bool { - debug!("should_inline({:?})", callsite); let tcx = self.tcx; if callsite.fn_sig.c_variadic() { @@ -217,6 +281,11 @@ impl Inliner<'tcx> { return false; } + if self.codegen_fn_attrs.instruction_set != codegen_fn_attrs.instruction_set { + debug!("`callee has incompatible instruction set - not inlining"); + return false; + } + let hinted = match codegen_fn_attrs.inline { // Just treat inline(always) as a hint for now, // there are cases that prevent inlining that we @@ -324,7 +393,9 @@ impl Inliner<'tcx> { if let Ok(Some(instance)) = Instance::resolve(self.tcx, self.param_env, def_id, substs) { - if callsite.callee == instance || self.history.contains(&instance) { + if callsite.callee.def_id() == instance.def_id() + || self.history.contains(&instance) + { debug!("`callee is recursive - not inlining"); return false; } @@ -373,7 +444,7 @@ impl Inliner<'tcx> { // Cost of the var is the size in machine-words, if we know // it. if let Some(size) = type_size_of(tcx, self.param_env, ty) { - cost += (size / ptr_size) as usize; + cost += ((size + ptr_size - 1) / ptr_size) as usize; } else { cost += UNKNOWN_SIZE_COST; } @@ -742,11 +813,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } fn visit_span(&mut self, span: &mut Span) { + let mut expn_data = + ExpnData::default(ExpnKind::Inlined, *span, self.tcx.sess.edition(), None); + expn_data.def_site = self.body_span; // Make sure that all spans track the fact that they were inlined. - *span = self.callsite_span.fresh_expansion(ExpnData { - def_site: self.body_span, - ..ExpnData::default(ExpnKind::Inlined, *span, self.tcx.sess.edition(), None) - }); + *span = self.callsite_span.fresh_expansion(expn_data); } fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { diff --git a/compiler/rustc_mir/src/transform/inline/cycle.rs b/compiler/rustc_mir/src/transform/inline/cycle.rs new file mode 100644 index 0000000000..e4d403fbf6 --- /dev/null +++ b/compiler/rustc_mir/src/transform/inline/cycle.rs @@ -0,0 +1,157 @@ +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::mir::TerminatorKind; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{self, subst::SubstsRef, InstanceDef, TyCtxt}; + +// FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking +// this query riddiculously often. +#[instrument(skip(tcx, root, target))] +crate fn mir_callgraph_reachable( + tcx: TyCtxt<'tcx>, + (root, target): (ty::Instance<'tcx>, LocalDefId), +) -> bool { + trace!(%root, target = %tcx.def_path_str(target.to_def_id())); + let param_env = tcx.param_env_reveal_all_normalized(target); + assert_ne!( + root.def_id().expect_local(), + target, + "you should not call `mir_callgraph_reachable` on immediate self recursion" + ); + assert!( + matches!(root.def, InstanceDef::Item(_)), + "you should not call `mir_callgraph_reachable` on shims" + ); + assert!( + !tcx.is_constructor(root.def_id()), + "you should not call `mir_callgraph_reachable` on enum/struct constructor functions" + ); + #[instrument(skip(tcx, param_env, target, stack, seen, recursion_limiter, caller))] + fn process( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + caller: ty::Instance<'tcx>, + target: LocalDefId, + stack: &mut Vec>, + seen: &mut FxHashSet>, + recursion_limiter: &mut FxHashMap, + ) -> bool { + trace!(%caller); + for &(callee, substs) in tcx.mir_inliner_callees(caller.def) { + let substs = caller.subst_mir_and_normalize_erasing_regions(tcx, param_env, substs); + let callee = match ty::Instance::resolve(tcx, param_env, callee, substs).unwrap() { + Some(callee) => callee, + None => { + trace!(?callee, "cannot resolve, skipping"); + continue; + } + }; + + // Found a path. + if callee.def_id() == target.to_def_id() { + return true; + } + + if tcx.is_constructor(callee.def_id()) { + trace!("constructors always have MIR"); + // Constructor functions cannot cause a query cycle. + continue; + } + + match callee.def { + InstanceDef::Item(_) => { + // If there is no MIR available (either because it was not in metadata or + // because it has no MIR because it's an extern function), then the inliner + // won't cause cycles on this. + if !tcx.is_mir_available(callee.def_id()) { + trace!(?callee, "no mir available, skipping"); + continue; + } + } + // These have no own callable MIR. + InstanceDef::Intrinsic(_) | InstanceDef::Virtual(..) => continue, + // These have MIR and if that MIR is inlined, substituted and then inlining is run + // again, a function item can end up getting inlined. Thus we'll be able to cause + // a cycle that way + InstanceDef::VtableShim(_) + | InstanceDef::ReifyShim(_) + | InstanceDef::FnPtrShim(..) + | InstanceDef::ClosureOnceShim { .. } + | InstanceDef::CloneShim(..) => {} + InstanceDef::DropGlue(..) => { + // FIXME: A not fully substituted drop shim can cause ICEs if one attempts to + // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this + // needs some more analysis. + if callee.needs_subst() { + continue; + } + } + } + + if seen.insert(callee) { + let recursion = recursion_limiter.entry(callee.def_id()).or_default(); + trace!(?callee, recursion = *recursion); + if tcx.sess.recursion_limit().value_within_limit(*recursion) { + *recursion += 1; + stack.push(callee); + let found_recursion = ensure_sufficient_stack(|| { + process(tcx, param_env, callee, target, stack, seen, recursion_limiter) + }); + if found_recursion { + return true; + } + stack.pop(); + } else { + // Pessimistically assume that there could be recursion. + return true; + } + } + } + false + } + process( + tcx, + param_env, + root, + target, + &mut Vec::new(), + &mut FxHashSet::default(), + &mut FxHashMap::default(), + ) +} + +crate fn mir_inliner_callees<'tcx>( + tcx: TyCtxt<'tcx>, + instance: ty::InstanceDef<'tcx>, +) -> &'tcx [(DefId, SubstsRef<'tcx>)] { + let steal; + let guard; + let body = match (instance, instance.def_id().as_local()) { + (InstanceDef::Item(_), Some(def_id)) => { + let def = ty::WithOptConstParam::unknown(def_id); + steal = tcx.mir_promoted(def).0; + guard = steal.borrow(); + &*guard + } + // Functions from other crates and MIR shims + _ => tcx.instance_mir(instance), + }; + let mut calls = Vec::new(); + for bb_data in body.basic_blocks() { + let terminator = bb_data.terminator(); + if let TerminatorKind::Call { func, .. } = &terminator.kind { + let ty = func.ty(&body.local_decls, tcx); + let call = match ty.kind() { + ty::FnDef(def_id, substs) => (*def_id, *substs), + _ => continue, + }; + // We've seen this before + if calls.contains(&call) { + continue; + } + calls.push(call); + } + } + tcx.arena.alloc_slice(&calls) +} diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs index 3eb2b500d6..74dadb2572 100644 --- a/compiler/rustc_mir/src/transform/instcombine.rs +++ b/compiler/rustc_mir/src/transform/instcombine.rs @@ -1,312 +1,123 @@ //! Performs various peephole optimizations. use crate::transform::MirPass; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::Mutability; -use rustc_index::vec::Idx; use rustc_middle::mir::{ - visit::PlaceContext, - visit::{MutVisitor, Visitor}, - Statement, -}; -use rustc_middle::mir::{ - BinOp, Body, BorrowKind, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, - Rvalue, + BinOp, Body, Constant, LocalDecls, Operand, Place, ProjectionElem, Rvalue, SourceInfo, + StatementKind, }; use rustc_middle::ty::{self, TyCtxt}; -use std::mem; pub struct InstCombine; impl<'tcx> MirPass<'tcx> for InstCombine { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - // First, find optimization opportunities. This is done in a pre-pass to keep the MIR - // read-only so that we can do global analyses on the MIR in the process (e.g. - // `Place::ty()`). - let optimizations = { - let mut optimization_finder = OptimizationFinder::new(body, tcx); - optimization_finder.visit_body(body); - optimization_finder.optimizations - }; - - // Then carry out those optimizations. - MutVisitor::visit_body(&mut InstCombineVisitor { optimizations, tcx }, body); - } -} - -pub struct InstCombineVisitor<'tcx> { - optimizations: OptimizationList<'tcx>, - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> InstCombineVisitor<'tcx> { - fn should_combine(&self, rvalue: &Rvalue<'tcx>, location: Location) -> bool { - self.tcx.consider_optimizing(|| { - format!("InstCombine - Rvalue: {:?} Location: {:?}", rvalue, location) - }) - } -} - -impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { - if self.optimizations.and_stars.remove(&location) && self.should_combine(rvalue, location) { - debug!("replacing `&*`: {:?}", rvalue); - let new_place = match rvalue { - Rvalue::Ref(_, _, place) => { - if let &[ref proj_l @ .., proj_r] = place.projection.as_ref() { - place.projection = self.tcx().intern_place_elems(&[proj_r]); - - Place { - // Replace with dummy - local: mem::replace(&mut place.local, Local::new(0)), - projection: self.tcx().intern_place_elems(proj_l), - } - } else { - unreachable!(); + let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); + let ctx = InstCombineContext { tcx, local_decls }; + for block in basic_blocks.iter_mut() { + for statement in block.statements.iter_mut() { + match statement.kind { + StatementKind::Assign(box (_place, ref mut rvalue)) => { + ctx.combine_bool_cmp(&statement.source_info, rvalue); + ctx.combine_ref_deref(&statement.source_info, rvalue); + ctx.combine_len(&statement.source_info, rvalue); } + _ => {} } - _ => bug!("Detected `&*` but didn't find `&*`!"), - }; - *rvalue = Rvalue::Use(Operand::Copy(new_place)) - } - - if let Some(constant) = self.optimizations.arrays_lengths.remove(&location) { - if self.should_combine(rvalue, location) { - debug!("replacing `Len([_; N])`: {:?}", rvalue); - *rvalue = Rvalue::Use(Operand::Constant(box constant)); - } - } - - if let Some(operand) = self.optimizations.unneeded_equality_comparison.remove(&location) { - if self.should_combine(rvalue, location) { - debug!("replacing {:?} with {:?}", rvalue, operand); - *rvalue = Rvalue::Use(operand); } } - - if let Some(place) = self.optimizations.unneeded_deref.remove(&location) { - if self.should_combine(rvalue, location) { - debug!("unneeded_deref: replacing {:?} with {:?}", rvalue, place); - *rvalue = Rvalue::Use(Operand::Copy(place)); - } - } - - self.super_rvalue(rvalue, location) } } -struct MutatingUseVisitor { - has_mutating_use: bool, - local_to_look_for: Local, -} - -impl MutatingUseVisitor { - fn has_mutating_use_in_stmt(local: Local, stmt: &Statement<'tcx>, location: Location) -> bool { - let mut _self = Self { has_mutating_use: false, local_to_look_for: local }; - _self.visit_statement(stmt, location); - _self.has_mutating_use - } -} - -impl<'tcx> Visitor<'tcx> for MutatingUseVisitor { - fn visit_local(&mut self, local: &Local, context: PlaceContext, _: Location) { - if *local == self.local_to_look_for { - self.has_mutating_use |= context.is_mutating_use(); - } - } -} - -/// Finds optimization opportunities on the MIR. -struct OptimizationFinder<'b, 'tcx> { - body: &'b Body<'tcx>, +struct InstCombineContext<'tcx, 'a> { tcx: TyCtxt<'tcx>, - optimizations: OptimizationList<'tcx>, + local_decls: &'a LocalDecls<'tcx>, } -impl OptimizationFinder<'b, 'tcx> { - fn new(body: &'b Body<'tcx>, tcx: TyCtxt<'tcx>) -> OptimizationFinder<'b, 'tcx> { - OptimizationFinder { body, tcx, optimizations: OptimizationList::default() } +impl<'tcx, 'a> InstCombineContext<'tcx, 'a> { + fn should_combine(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool { + self.tcx.consider_optimizing(|| { + format!("InstCombine - Rvalue: {:?} SourceInfo: {:?}", rvalue, source_info) + }) } - fn find_deref_of_address(&mut self, rvalue: &Rvalue<'tcx>, location: Location) -> Option<()> { - // FIXME(#78192): This optimization can result in unsoundness. - if !self.tcx.sess.opts.debugging_opts.unsound_mir_opts { - return None; - } - - // Look for the sequence - // - // _2 = &_1; - // ... - // _5 = (*_2); - // - // which we can replace the last statement with `_5 = _1;` to avoid the load of `_2`. - if let Rvalue::Use(op) = rvalue { - let local_being_derefed = match op.place()?.as_ref() { - PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), - _ => None, - }?; - - let mut dead_locals_seen = vec![]; - - let stmt_index = location.statement_index; - // Look behind for statement that assigns the local from a address of operator. - // 6 is chosen as a heuristic determined by seeing the number of times - // the optimization kicked in compiling rust std. - let lower_index = stmt_index.saturating_sub(6); - let statements_to_look_in = self.body.basic_blocks()[location.block].statements - [lower_index..stmt_index] - .iter() - .rev(); - for stmt in statements_to_look_in { - match &stmt.kind { - // Exhaustive match on statements to detect conditions that warrant we bail out of the optimization. - rustc_middle::mir::StatementKind::Assign(box (l, r)) - if l.local == local_being_derefed => - { - match r { - // Looking for immutable reference e.g _local_being_deref = &_1; - Rvalue::Ref( - _, - // Only apply the optimization if it is an immutable borrow. - BorrowKind::Shared, - place_taken_address_of, - ) => { - // Make sure that the place has not been marked dead - if dead_locals_seen.contains(&place_taken_address_of.local) { - return None; - } - - self.optimizations - .unneeded_deref - .insert(location, *place_taken_address_of); - return Some(()); - } - - // We found an assignment of `local_being_deref` that is not an immutable ref, e.g the following sequence - // _2 = &_1; - // _3 = &5 - // _2 = _3; <-- this means it is no longer valid to replace the last statement with `_5 = _1;` - // _5 = (*_2); - _ => return None, - } - } - - // Inline asm can do anything, so bail out of the optimization. - rustc_middle::mir::StatementKind::LlvmInlineAsm(_) => return None, - - // Remember `StorageDead`s, as the local being marked dead could be the - // place RHS we are looking for, in which case we need to abort to avoid UB - // using an uninitialized place - rustc_middle::mir::StatementKind::StorageDead(dead) => { - dead_locals_seen.push(*dead) - } - - // Check that `local_being_deref` is not being used in a mutating way which can cause misoptimization. - rustc_middle::mir::StatementKind::Assign(box (_, _)) - | rustc_middle::mir::StatementKind::Coverage(_) - | rustc_middle::mir::StatementKind::Nop - | rustc_middle::mir::StatementKind::FakeRead(_, _) - | rustc_middle::mir::StatementKind::StorageLive(_) - | rustc_middle::mir::StatementKind::Retag(_, _) - | rustc_middle::mir::StatementKind::AscribeUserType(_, _) - | rustc_middle::mir::StatementKind::SetDiscriminant { .. } => { - if MutatingUseVisitor::has_mutating_use_in_stmt( - local_being_derefed, - stmt, - location, - ) { - return None; - } + /// Transform boolean comparisons into logical operations. + fn combine_bool_cmp(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + match rvalue { + Rvalue::BinaryOp(op @ (BinOp::Eq | BinOp::Ne), a, b) => { + let new = match (op, self.try_eval_bool(a), self.try_eval_bool(b)) { + // Transform "Eq(a, true)" ==> "a" + (BinOp::Eq, _, Some(true)) => Some(a.clone()), + + // Transform "Ne(a, false)" ==> "a" + (BinOp::Ne, _, Some(false)) => Some(a.clone()), + + // Transform "Eq(true, b)" ==> "b" + (BinOp::Eq, Some(true), _) => Some(b.clone()), + + // Transform "Ne(false, b)" ==> "b" + (BinOp::Ne, Some(false), _) => Some(b.clone()), + + // FIXME: Consider combining remaining comparisons into logical operations: + // Transform "Eq(false, b)" ==> "Not(b)" + // Transform "Ne(true, b)" ==> "Not(b)" + // Transform "Eq(a, false)" ==> "Not(a)" + // Transform "Ne(a, true)" ==> "Not(a)" + _ => None, + }; + + if let Some(new) = new { + if self.should_combine(source_info, rvalue) { + *rvalue = Rvalue::Use(new); } } } - } - Some(()) - } - fn find_unneeded_equality_comparison(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { - // find Ne(_place, false) or Ne(false, _place) - // or Eq(_place, true) or Eq(true, _place) - if let Rvalue::BinaryOp(op, l, r) = rvalue { - let const_to_find = if *op == BinOp::Ne { - false - } else if *op == BinOp::Eq { - true - } else { - return; - }; - // (const, _place) - if let Some(o) = self.find_operand_in_equality_comparison_pattern(l, r, const_to_find) { - self.optimizations.unneeded_equality_comparison.insert(location, o.clone()); - } - // (_place, const) - else if let Some(o) = - self.find_operand_in_equality_comparison_pattern(r, l, const_to_find) - { - self.optimizations.unneeded_equality_comparison.insert(location, o.clone()); - } + _ => {} } } - fn find_operand_in_equality_comparison_pattern( - &self, - l: &Operand<'tcx>, - r: &'a Operand<'tcx>, - const_to_find: bool, - ) -> Option<&'a Operand<'tcx>> { - let const_ = l.constant()?; - if const_.literal.ty == self.tcx.types.bool - && const_.literal.val.try_to_bool() == Some(const_to_find) - { - if r.place().is_some() { - return Some(r); - } - } - - None + fn try_eval_bool(&self, a: &Operand<'_>) -> Option { + let a = a.constant()?; + if a.literal.ty.is_bool() { a.literal.val.try_to_bool() } else { None } } -} -impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + /// Transform "&(*a)" ==> "a". + fn combine_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Ref(_, _, place) = rvalue { - if let PlaceRef { local, projection: &[ref proj_base @ .., ProjectionElem::Deref] } = - place.as_ref() - { - // The dereferenced place must have type `&_`. - let ty = Place::ty_from(local, proj_base, self.body, self.tcx).ty; - if let ty::Ref(_, _, Mutability::Not) = ty.kind() { - self.optimizations.and_stars.insert(location); + if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() { + if let ty::Ref(_, _, Mutability::Not) = + base.ty(self.local_decls, self.tcx).ty.kind() + { + // The dereferenced place must have type `&_`, so that we don't copy `&mut _`. + } else { + return; } + + if !self.should_combine(source_info, rvalue) { + return; + } + + *rvalue = Rvalue::Use(Operand::Copy(Place { + local: base.local, + projection: self.tcx.intern_place_elems(base.projection), + })); } } + } + /// Transform "Len([_; N])" ==> "N". + fn combine_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Len(ref place) = *rvalue { - let place_ty = place.ty(&self.body.local_decls, self.tcx).ty; + let place_ty = place.ty(self.local_decls, self.tcx).ty; if let ty::Array(_, len) = place_ty.kind() { - let span = self.body.source_info(location).span; - let constant = Constant { span, literal: len, user_ty: None }; - self.optimizations.arrays_lengths.insert(location, constant); + if !self.should_combine(source_info, rvalue) { + return; + } + + let constant = Constant { span: source_info.span, literal: len, user_ty: None }; + *rvalue = Rvalue::Use(Operand::Constant(box constant)); } } - - let _ = self.find_deref_of_address(rvalue, location); - - self.find_unneeded_equality_comparison(rvalue, location); - - self.super_rvalue(rvalue, location) } } - -#[derive(Default)] -struct OptimizationList<'tcx> { - and_stars: FxHashSet, - arrays_lengths: FxHashMap>, - unneeded_equality_comparison: FxHashMap>, - unneeded_deref: FxHashMap>, -} diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index 7f3b421cf7..2786127513 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -71,9 +71,11 @@ pub(crate) fn provide(providers: &mut Providers) { }, mir_promoted, mir_drops_elaborated_and_const_checked, + mir_for_ctfe, + mir_for_ctfe_of_const_arg, optimized_mir, - optimized_mir_of_const_arg, is_mir_available, + is_ctfe_mir_available: |tcx, did| is_mir_available(tcx, did), promoted_mir: |tcx, def_id| { let def_id = def_id.expect_local(); if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) { @@ -278,6 +280,7 @@ fn mir_const<'tcx>( tcx.alloc_steal_mir(body) } +/// Compute the main MIR body and the list of MIR bodies of the promoteds. fn mir_promoted( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam, @@ -319,6 +322,87 @@ fn mir_promoted( (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted)) } +/// Compute the MIR that is used during CTFE (and thus has no optimizations run on it) +fn mir_for_ctfe<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx Body<'tcx> { + let did = def_id.expect_local(); + if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) { + tcx.mir_for_ctfe_of_const_arg(def) + } else { + tcx.arena.alloc(inner_mir_for_ctfe(tcx, ty::WithOptConstParam::unknown(did))) + } +} + +/// Same as `mir_for_ctfe`, but used to get the MIR of a const generic parameter. +/// The docs on `WithOptConstParam` explain this a bit more, but the TLDR is that +/// we'd get cycle errors with `mir_for_ctfe`, because typeck would need to typeck +/// the const parameter while type checking the main body, which in turn would try +/// to type check the main body again. +fn mir_for_ctfe_of_const_arg<'tcx>( + tcx: TyCtxt<'tcx>, + (did, param_did): (LocalDefId, DefId), +) -> &'tcx Body<'tcx> { + tcx.arena.alloc(inner_mir_for_ctfe( + tcx, + ty::WithOptConstParam { did, const_param_did: Some(param_did) }, + )) +} + +fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_> { + // FIXME: don't duplicate this between the optimized_mir/mir_for_ctfe queries + if tcx.is_constructor(def.did.to_def_id()) { + // There's no reason to run all of the MIR passes on constructors when + // we can just output the MIR we want directly. This also saves const + // qualification and borrow checking the trouble of special casing + // constructors. + return shim::build_adt_ctor(tcx, def.did.to_def_id()); + } + + let context = tcx + .hir() + .body_const_context(def.did) + .expect("mir_for_ctfe should not be used for runtime functions"); + + let mut body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone(); + + match context { + // Do not const prop functions, either they get executed at runtime or exported to metadata, + // so we run const prop on them, or they don't, in which case we const evaluate some control + // flow paths of the function and any errors in those paths will get emitted as const eval + // errors. + hir::ConstContext::ConstFn => {} + // Static items always get evaluated, so we can just let const eval see if any erroneous + // control flow paths get executed. + hir::ConstContext::Static(_) => {} + // Associated constants get const prop run so we detect common failure situations in the + // crate that defined the constant. + // Technically we want to not run on regular const items, but oli-obk doesn't know how to + // conveniently detect that at this point without looking at the HIR. + hir::ConstContext::Const => { + #[rustfmt::skip] + let optimizations: &[&dyn MirPass<'_>] = &[ + &const_prop::ConstProp, + ]; + + #[rustfmt::skip] + run_passes( + tcx, + &mut body, + MirPhase::Optimization, + &[ + optimizations, + ], + ); + } + } + + debug_assert!(!body.has_free_regions(), "Free regions in MIR for CTFE"); + + body +} + +/// Obtain just the main MIR (no promoteds) and run some cleanups on it. This also runs +/// mir borrowck *before* doing so in order to ensure that borrowck can be run and doesn't +/// end up missing the source MIR due to stealing happening. fn mir_drops_elaborated_and_const_checked<'tcx>( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam, @@ -335,6 +419,20 @@ fn mir_drops_elaborated_and_const_checked<'tcx>( tcx.ensure().mir_borrowck(def.did); } + let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); + use rustc_middle::hir::map::blocks::FnLikeNode; + let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + if is_fn_like { + let did = def.did.to_def_id(); + let def = ty::WithOptConstParam::unknown(did); + + // Do not compute the mir call graph without said call graph actually being used. + // Keep this in sync with the mir inliner's optimization level. + if tcx.sess.opts.debugging_opts.mir_opt_level >= 2 { + let _ = tcx.mir_inliner_callees(ty::InstanceDef::Item(def)); + } + } + let (body, _) = tcx.mir_promoted(def); let mut body = body.steal(); @@ -349,7 +447,7 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[ // Remove all things only needed by analysis - &no_landing_pads::NoLandingPads::new(tcx), + &no_landing_pads::NoLandingPads, &simplify_branches::SimplifyBranches::new("initial"), &remove_noop_landing_pads::RemoveNoopLandingPads, &cleanup_post_borrowck::CleanupNonCodegenStatements, @@ -357,7 +455,7 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc // These next passes must be executed together &add_call_guards::CriticalCallEdges, &elaborate_drops::ElaborateDrops, - &no_landing_pads::NoLandingPads::new(tcx), + &no_landing_pads::NoLandingPads, // AddMovesForPackedDrops needs to run after drop // elaboration. &add_moves_for_packed_drops::AddMovesForPackedDrops, @@ -456,35 +554,32 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { ); } +/// Optimize the MIR and prepare it for codegen. fn optimized_mir<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx Body<'tcx> { let did = did.expect_local(); - if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) { - tcx.optimized_mir_of_const_arg(def) - } else { - tcx.arena.alloc(inner_optimized_mir(tcx, ty::WithOptConstParam::unknown(did))) - } + assert_eq!(ty::WithOptConstParam::try_lookup(did, tcx), None); + tcx.arena.alloc(inner_optimized_mir(tcx, did)) } -fn optimized_mir_of_const_arg<'tcx>( - tcx: TyCtxt<'tcx>, - (did, param_did): (LocalDefId, DefId), -) -> &'tcx Body<'tcx> { - tcx.arena.alloc(inner_optimized_mir( - tcx, - ty::WithOptConstParam { did, const_param_did: Some(param_did) }, - )) -} - -fn inner_optimized_mir(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_> { - if tcx.is_constructor(def.did.to_def_id()) { +fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { + if tcx.is_constructor(did.to_def_id()) { // There's no reason to run all of the MIR passes on constructors when // we can just output the MIR we want directly. This also saves const // qualification and borrow checking the trouble of special casing // constructors. - return shim::build_adt_ctor(tcx, def.did.to_def_id()); + return shim::build_adt_ctor(tcx, did.to_def_id()); } - let mut body = tcx.mir_drops_elaborated_and_const_checked(def).steal(); + match tcx.hir().body_const_context(did) { + // Run the `mir_for_ctfe` query, which depends on `mir_drops_elaborated_and_const_checked` + // which we are going to steal below. Thus we need to run `mir_for_ctfe` first, so it + // computes and caches its result. + Some(hir::ConstContext::ConstFn) => tcx.ensure().mir_for_ctfe(did), + None => {} + Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other), + } + let mut body = + tcx.mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(did)).steal(); run_optimization_passes(tcx, &mut body); debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR"); @@ -492,6 +587,8 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) body } +/// Fetch all the promoteds of an item and prepare their MIR bodies to be ready for +/// constant evaluation once all substitutions become known. fn promoted_mir<'tcx>( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam, @@ -510,7 +607,6 @@ fn promoted_mir<'tcx>( for body in &mut promoted { run_post_borrowck_cleanup_passes(tcx, body); - run_optimization_passes(tcx, body); } debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR"); diff --git a/compiler/rustc_mir/src/transform/no_landing_pads.rs b/compiler/rustc_mir/src/transform/no_landing_pads.rs index 83954c93c0..5479f0cc58 100644 --- a/compiler/rustc_mir/src/transform/no_landing_pads.rs +++ b/compiler/rustc_mir/src/transform/no_landing_pads.rs @@ -2,42 +2,27 @@ //! specified. use crate::transform::MirPass; -use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_target::spec::PanicStrategy; -pub struct NoLandingPads<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> NoLandingPads<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> Self { - NoLandingPads { tcx } - } -} +pub struct NoLandingPads; -impl<'tcx> MirPass<'tcx> for NoLandingPads<'tcx> { +impl<'tcx> MirPass<'tcx> for NoLandingPads { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { no_landing_pads(tcx, body) } } pub fn no_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.panic_strategy() == PanicStrategy::Abort { - NoLandingPads::new(tcx).visit_body(body); - } -} - -impl<'tcx> MutVisitor<'tcx> for NoLandingPads<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx + if tcx.sess.panic_strategy() != PanicStrategy::Abort { + return; } - fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { + for block in body.basic_blocks_mut() { + let terminator = block.terminator_mut(); if let Some(unwind) = terminator.kind.unwind_mut() { unwind.take(); } - self.super_terminator(terminator, location); } } diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index 8d5ed747c3..b4504a0e22 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -90,7 +90,7 @@ pub enum TempState { impl TempState { pub fn is_promotable(&self) -> bool { debug!("is_promotable: self={:?}", self); - matches!(self, TempState::Defined { .. } ) + matches!(self, TempState::Defined { .. }) } } @@ -102,9 +102,6 @@ pub enum Candidate { /// Borrow of a constant temporary, candidate for lifetime extension. Ref(Location), - /// Promotion of the `x` in `[x; 32]`. - Repeat(Location), - /// Currently applied to function calls where the callee has the unstable /// `#[rustc_args_required_const]` attribute as well as the SIMD shuffle /// intrinsic. The intrinsic requires the arguments are indeed constant and @@ -120,14 +117,14 @@ impl Candidate { /// Returns `true` if we should use the "explicit" rules for promotability for this `Candidate`. fn forces_explicit_promotion(&self) -> bool { match self { - Candidate::Ref(_) | Candidate::Repeat(_) => false, + Candidate::Ref(_) => false, Candidate::Argument { .. } | Candidate::InlineAsm { .. } => true, } } fn source_info(&self, body: &Body<'_>) -> SourceInfo { match self { - Candidate::Ref(location) | Candidate::Repeat(location) => *body.source_info(*location), + Candidate::Ref(location) => *body.source_info(*location), Candidate::Argument { bb, .. } | Candidate::InlineAsm { bb, .. } => { *body.source_info(body.terminator_loc(*bb)) } @@ -213,11 +210,6 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> { Rvalue::Ref(..) => { self.candidates.push(Candidate::Ref(location)); } - Rvalue::Repeat(..) if self.ccx.tcx.features().const_in_array_repeat_expressions => { - // FIXME(#49147) only promote the element when it isn't `Copy` - // (so that code that can copy it at runtime is unaffected). - self.candidates.push(Candidate::Repeat(location)); - } _ => {} } } @@ -309,70 +301,31 @@ impl<'tcx> Validator<'_, 'tcx> { let statement = &self.body[loc.block].statements[loc.statement_index]; match &statement.kind { StatementKind::Assign(box (_, Rvalue::Ref(_, kind, place))) => { - match kind { - BorrowKind::Shared | BorrowKind::Mut { .. } => {} - - // FIXME(eddyb) these aren't promoted here but *could* - // be promoted as part of a larger value because - // `validate_rvalue` doesn't check them, need to - // figure out what is the intended behavior. - BorrowKind::Shallow | BorrowKind::Unique => return Err(Unpromotable), - } - // We can only promote interior borrows of promotable temps (non-temps // don't get promoted anyway). self.validate_local(place.local)?; + // The reference operation itself must be promotable. + // (Needs to come after `validate_local` to avoid ICEs.) + self.validate_ref(*kind, place)?; + + // We do not check all the projections (they do not get promoted anyway), + // but we do stay away from promoting anything involving a dereference. if place.projection.contains(&ProjectionElem::Deref) { return Err(Unpromotable); } - if self.qualif_local::(place.local) { - return Err(Unpromotable); - } - // FIXME(eddyb) this duplicates part of `validate_rvalue`. - let has_mut_interior = - self.qualif_local::(place.local); - if has_mut_interior { + // We cannot promote things that need dropping, since the promoted value + // would not get dropped. + if self.qualif_local::(place.local) { return Err(Unpromotable); } - if let BorrowKind::Mut { .. } = kind { - let ty = place.ty(self.body, self.tcx).ty; - - // In theory, any zero-sized value could be borrowed - // mutably without consequences. However, only &mut [] - // is allowed right now. - if let ty::Array(_, len) = ty.kind() { - match len.try_eval_usize(self.tcx, self.param_env) { - Some(0) => {} - _ => return Err(Unpromotable), - } - } else { - return Err(Unpromotable); - } - } - Ok(()) } _ => bug!(), } } - Candidate::Repeat(loc) => { - assert!(!self.explicit); - - let statement = &self.body[loc.block].statements[loc.statement_index]; - match &statement.kind { - StatementKind::Assign(box (_, Rvalue::Repeat(ref operand, _))) => { - if !self.tcx.features().const_in_array_repeat_expressions { - return Err(Unpromotable); - } - - self.validate_operand(operand) - } - _ => bug!(), - } - } Candidate::Argument { bb, index } => { assert!(self.explicit); @@ -439,10 +392,11 @@ impl<'tcx> Validator<'_, 'tcx> { // FIXME(eddyb) maybe cache this? fn validate_local(&self, local: Local) -> Result<(), Unpromotable> { if let TempState::Defined { location: loc, .. } = self.temps[local] { - let num_stmts = self.body[loc.block].statements.len(); + let block = &self.body[loc.block]; + let num_stmts = block.statements.len(); if loc.statement_index < num_stmts { - let statement = &self.body[loc.block].statements[loc.statement_index]; + let statement = &block.statements[loc.statement_index]; match &statement.kind { StatementKind::Assign(box (_, rhs)) => self.validate_rvalue(rhs), _ => { @@ -454,7 +408,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } } else { - let terminator = self.body[loc.block].terminator(); + let terminator = block.terminator(); match &terminator.kind { TerminatorKind::Call { func, args, .. } => self.validate_call(func, args), TerminatorKind::Yield { .. } => Err(Unpromotable), @@ -469,43 +423,43 @@ impl<'tcx> Validator<'_, 'tcx> { } fn validate_place(&self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> { - match place { - PlaceRef { local, projection: [] } => self.validate_local(local), - PlaceRef { local, projection: [proj_base @ .., elem] } => { + match place.last_projection() { + None => self.validate_local(place.local), + Some((place_base, elem)) => { // Validate topmost projection, then recurse. - match *elem { + match elem { ProjectionElem::Deref => { let mut promotable = false; - // This is a special treatment for cases like *&STATIC where STATIC is a - // global static variable. - // This pattern is generated only when global static variables are directly - // accessed and is qualified for promotion safely. - if let TempState::Defined { location, .. } = self.temps[local] { - let def_stmt = - self.body[location.block].statements.get(location.statement_index); - if let Some(Statement { - kind: - StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(c)))), - .. - }) = def_stmt - { - if let Some(did) = c.check_static_ptr(self.tcx) { - // Evaluating a promoted may not read statics except if it got - // promoted from a static (this is a CTFE check). So we - // can only promote static accesses inside statics. - if let Some(hir::ConstContext::Static(..)) = self.const_kind { - // The `is_empty` predicate is introduced to exclude the case - // where the projection operations are [ .field, * ]. - // The reason is because promotion will be illegal if field - // accesses precede the dereferencing. - // Discussion can be found at - // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247 - // There may be opportunity for generalization, but this needs to be - // accounted for. - if proj_base.is_empty() - && !self.tcx.is_thread_local_static(did) + // We need to make sure this is a `Deref` of a local with no further projections. + // Discussion can be found at + // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247 + if let Some(local) = place_base.as_local() { + // This is a special treatment for cases like *&STATIC where STATIC is a + // global static variable. + // This pattern is generated only when global static variables are directly + // accessed and is qualified for promotion safely. + if let TempState::Defined { location, .. } = self.temps[local] { + let def_stmt = self.body[location.block] + .statements + .get(location.statement_index); + if let Some(Statement { + kind: + StatementKind::Assign(box ( + _, + Rvalue::Use(Operand::Constant(c)), + )), + .. + }) = def_stmt + { + if let Some(did) = c.check_static_ptr(self.tcx) { + // Evaluating a promoted may not read statics except if it got + // promoted from a static (this is a CTFE check). So we + // can only promote static accesses inside statics. + if let Some(hir::ConstContext::Static(..)) = self.const_kind { - promotable = true; + if !self.tcx.is_thread_local_static(did) { + promotable = true; + } } } } @@ -522,12 +476,55 @@ impl<'tcx> Validator<'_, 'tcx> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {} ProjectionElem::Index(local) => { + if !self.explicit { + let mut promotable = false; + // Only accept if we can predict the index and are indexing an array. + let val = if let TempState::Defined { location: loc, .. } = + self.temps[local] + { + let block = &self.body[loc.block]; + if loc.statement_index < block.statements.len() { + let statement = &block.statements[loc.statement_index]; + match &statement.kind { + StatementKind::Assign(box ( + _, + Rvalue::Use(Operand::Constant(c)), + )) => c.literal.try_eval_usize(self.tcx, self.param_env), + _ => None, + } + } else { + None + } + } else { + None + }; + if let Some(idx) = val { + // Determine the type of the thing we are indexing. + let ty = place_base.ty(self.body, self.tcx).ty; + match ty.kind() { + ty::Array(_, len) => { + // It's an array; determine its length. + if let Some(len) = + len.try_eval_usize(self.tcx, self.param_env) + { + // If the index is in-bounds, go ahead. + if idx < len { + promotable = true; + } + } + } + _ => {} + } + } + if !promotable { + return Err(Unpromotable); + } + } self.validate_local(local)?; } ProjectionElem::Field(..) => { - let base_ty = - Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; + let base_ty = place_base.ty(self.body, self.tcx).ty; if let Some(def) = base_ty.ty_adt_def() { // No promotion of union field accesses. if def.is_union() { @@ -537,7 +534,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } - self.validate_place(PlaceRef { local: place.local, projection: proj_base }) + self.validate_place(place_base) } } } @@ -572,117 +569,177 @@ impl<'tcx> Validator<'_, 'tcx> { } } - fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { - match *rvalue { - Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { - let operand_ty = operand.ty(self.body, self.tcx); - let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); - let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); - if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) { - // ptr-to-int casts are not possible in consts and thus not promotable + fn validate_ref(&self, kind: BorrowKind, place: &Place<'tcx>) -> Result<(), Unpromotable> { + match kind { + // Reject these borrow types just to be safe. + // FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase. + BorrowKind::Shallow | BorrowKind::Unique => return Err(Unpromotable), + + BorrowKind::Shared => { + let has_mut_interior = self.qualif_local::(place.local); + if has_mut_interior { return Err(Unpromotable); } } - Rvalue::BinaryOp(op, ref lhs, _) => { - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind() { - assert!( - op == BinOp::Eq - || op == BinOp::Ne - || op == BinOp::Le - || op == BinOp::Lt - || op == BinOp::Ge - || op == BinOp::Gt - || op == BinOp::Offset - ); - - // raw pointer operations are not allowed inside consts and thus not promotable + BorrowKind::Mut { .. } => { + let ty = place.ty(self.body, self.tcx).ty; + + // In theory, any zero-sized value could be borrowed + // mutably without consequences. However, only &mut [] + // is allowed right now. + if let ty::Array(_, len) = ty.kind() { + match len.try_eval_usize(self.tcx, self.param_env) { + Some(0) => {} + _ => return Err(Unpromotable), + } + } else { return Err(Unpromotable); } } - - Rvalue::NullaryOp(NullOp::Box, _) => return Err(Unpromotable), - - // FIXME(RalfJung): the rest is *implicitly considered promotable*... that seems dangerous. - _ => {} } + Ok(()) + } + + fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { match rvalue { - Rvalue::ThreadLocalRef(_) => Err(Unpromotable), + Rvalue::Use(operand) | Rvalue::Repeat(operand, _) => { + self.validate_operand(operand)?; + } + + Rvalue::Discriminant(place) | Rvalue::Len(place) => { + self.validate_place(place.as_ref())? + } + + Rvalue::ThreadLocalRef(_) => return Err(Unpromotable), + + Rvalue::Cast(kind, operand, cast_ty) => { + if matches!(kind, CastKind::Misc) { + let operand_ty = operand.ty(self.body, self.tcx); + let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); + let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); + if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) { + // ptr-to-int casts are not possible in consts and thus not promotable + return Err(Unpromotable); + } + // int-to-ptr casts are fine, they just use the integer value at pointer type. + } + + self.validate_operand(operand)?; + } + + Rvalue::NullaryOp(op, _) => match op { + NullOp::Box => return Err(Unpromotable), + NullOp::SizeOf => {} + }, - Rvalue::NullaryOp(..) => Ok(()), + Rvalue::UnaryOp(op, operand) => { + match op { + // These operations can never fail. + UnOp::Neg | UnOp::Not => {} + } - Rvalue::Discriminant(place) | Rvalue::Len(place) => self.validate_place(place.as_ref()), + self.validate_operand(operand)?; + } + + Rvalue::BinaryOp(op, lhs, rhs) | Rvalue::CheckedBinaryOp(op, lhs, rhs) => { + let op = *op; + let lhs_ty = lhs.ty(self.body, self.tcx); + + if let ty::RawPtr(_) | ty::FnPtr(..) = lhs_ty.kind() { + // Raw and fn pointer operations are not allowed inside consts and thus not promotable. + assert!(matches!( + op, + BinOp::Eq + | BinOp::Ne + | BinOp::Le + | BinOp::Lt + | BinOp::Ge + | BinOp::Gt + | BinOp::Offset + )); + return Err(Unpromotable); + } - Rvalue::Use(operand) - | Rvalue::Repeat(operand, _) - | Rvalue::UnaryOp(_, operand) - | Rvalue::Cast(_, operand, _) => self.validate_operand(operand), + match op { + BinOp::Div | BinOp::Rem => { + if !self.explicit && lhs_ty.is_integral() { + // Integer division: the RHS must be a non-zero const. + let const_val = match rhs { + Operand::Constant(c) => { + c.literal.try_eval_bits(self.tcx, self.param_env, lhs_ty) + } + _ => None, + }; + match const_val { + Some(x) if x != 0 => {} // okay + _ => return Err(Unpromotable), // value not known or 0 -- not okay + } + } + } + // The remaining operations can never fail. + BinOp::Eq + | BinOp::Ne + | BinOp::Le + | BinOp::Lt + | BinOp::Ge + | BinOp::Gt + | BinOp::Offset + | BinOp::Add + | BinOp::Sub + | BinOp::Mul + | BinOp::BitXor + | BinOp::BitAnd + | BinOp::BitOr + | BinOp::Shl + | BinOp::Shr => {} + } - Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { self.validate_operand(lhs)?; - self.validate_operand(rhs) + self.validate_operand(rhs)?; } Rvalue::AddressOf(_, place) => { // We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is // no problem, only using it is. - if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { - let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; + if let Some((place_base, ProjectionElem::Deref)) = place.as_ref().last_projection() + { + let base_ty = place_base.ty(self.body, self.tcx).ty; if let ty::Ref(..) = base_ty.kind() { - return self.validate_place(PlaceRef { - local: place.local, - projection: proj_base, - }); + return self.validate_place(place_base); } } - Err(Unpromotable) + return Err(Unpromotable); } Rvalue::Ref(_, kind, place) => { - if let BorrowKind::Mut { .. } = kind { - let ty = place.ty(self.body, self.tcx).ty; - - // In theory, any zero-sized value could be borrowed - // mutably without consequences. However, only &mut [] - // is allowed right now. - if let ty::Array(_, len) = ty.kind() { - match len.try_eval_usize(self.tcx, self.param_env) { - Some(0) => {} - _ => return Err(Unpromotable), - } - } else { - return Err(Unpromotable); - } - } - // Special-case reborrows to be more like a copy of the reference. - let mut place = place.as_ref(); - if let [proj_base @ .., ProjectionElem::Deref] = &place.projection { - let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; + let mut place_simplified = place.as_ref(); + if let Some((place_base, ProjectionElem::Deref)) = + place_simplified.last_projection() + { + let base_ty = place_base.ty(self.body, self.tcx).ty; if let ty::Ref(..) = base_ty.kind() { - place = PlaceRef { local: place.local, projection: proj_base }; + place_simplified = place_base; } } - self.validate_place(place)?; + self.validate_place(place_simplified)?; - let has_mut_interior = self.qualif_local::(place.local); - if has_mut_interior { - return Err(Unpromotable); - } - - Ok(()) + // Check that the reference is fine (using the original place!). + // (Needs to come after `validate_place` to avoid ICEs.) + self.validate_ref(*kind, place)?; } - Rvalue::Aggregate(_, ref operands) => { + Rvalue::Aggregate(_, operands) => { for o in operands { self.validate_operand(o)?; } - - Ok(()) } } + + Ok(()) } fn validate_call( @@ -1010,18 +1067,6 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { _ => bug!(), } } - Candidate::Repeat(loc) => { - let statement = &mut blocks[loc.block].statements[loc.statement_index]; - match statement.kind { - StatementKind::Assign(box (_, Rvalue::Repeat(ref mut operand, _))) => { - let ty = operand.ty(local_decls, self.tcx); - let span = statement.source_info.span; - - Rvalue::Use(mem::replace(operand, promoted_operand(ty, span))) - } - _ => bug!(), - } - } Candidate::Argument { bb, index } => { let terminator = blocks[bb].terminator_mut(); match terminator.kind { @@ -1102,8 +1147,7 @@ pub fn promote_candidates<'tcx>( let mut extra_statements = vec![]; for candidate in candidates.into_iter().rev() { match candidate { - Candidate::Repeat(Location { block, statement_index }) - | Candidate::Ref(Location { block, statement_index }) => { + Candidate::Ref(Location { block, statement_index }) => { if let StatementKind::Assign(box (place, _)) = &body[block].statements[statement_index].kind { @@ -1187,27 +1231,3 @@ pub fn promote_candidates<'tcx>( promotions } - -/// This function returns `true` if the `const_in_array_repeat_expressions` feature attribute should -/// be suggested. This function is probably quite expensive, it shouldn't be run in the happy path. -/// Feature attribute should be suggested if `operand` can be promoted and the feature is not -/// enabled. -crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>( - ccx: &ConstCx<'_, 'tcx>, - operand: &Operand<'tcx>, -) -> bool { - let mut rpo = traversal::reverse_postorder(&ccx.body); - let (temps, _) = collect_temps_and_candidates(&ccx, &mut rpo); - let validator = Validator { ccx, temps: &temps, explicit: false }; - - let should_promote = validator.validate_operand(operand).is_ok(); - let feature_flag = validator.ccx.tcx.features().const_in_array_repeat_expressions; - debug!( - "should_suggest_const_in_array_repeat_expressions_flag: def_id={:?} \ - should_promote={:?} feature_flag={:?}", - validator.ccx.def_id(), - should_promote, - feature_flag - ); - should_promote && !feature_flag -} diff --git a/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs b/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs index 221114eeba..5144d48750 100644 --- a/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs +++ b/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs @@ -1,9 +1,8 @@ //! This pass replaces a drop of a type that does not need dropping, with a goto use crate::transform::MirPass; -use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; -use rustc_middle::ty::{ParamEnv, TyCtxt}; +use rustc_middle::ty::TyCtxt; use super::simplify::simplify_cfg; @@ -12,24 +11,26 @@ pub struct RemoveUnneededDrops; impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running RemoveUnneededDrops on {:?}", body.source); - let mut opt_finder = RemoveUnneededDropsOptimizationFinder { - tcx, - body, - param_env: tcx.param_env(body.source.def_id()), - optimizations: vec![], - }; - opt_finder.visit_body(body); - let should_simplify = !opt_finder.optimizations.is_empty(); - for (loc, target) in opt_finder.optimizations { - if !tcx - .consider_optimizing(|| format!("RemoveUnneededDrops {:?} ", body.source.def_id())) - { - break; - } - let terminator = body.basic_blocks_mut()[loc.block].terminator_mut(); - debug!("SUCCESS: replacing `drop` with goto({:?})", target); - terminator.kind = TerminatorKind::Goto { target }; + let did = body.source.def_id(); + let param_env = tcx.param_env(did); + let mut should_simplify = false; + + let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); + for block in basic_blocks { + let terminator = block.terminator_mut(); + if let TerminatorKind::Drop { place, target, .. } = terminator.kind { + let ty = place.ty(local_decls, tcx); + if ty.ty.needs_drop(tcx, param_env) { + continue; + } + if !tcx.consider_optimizing(|| format!("RemoveUnneededDrops {:?} ", did)) { + continue; + } + debug!("SUCCESS: replacing `drop` with goto({:?})", target); + terminator.kind = TerminatorKind::Goto { target }; + should_simplify = true; + } } // if we applied optimizations, we potentially have some cfg to cleanup to @@ -39,25 +40,3 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops { } } } - -impl<'a, 'tcx> Visitor<'tcx> for RemoveUnneededDropsOptimizationFinder<'a, 'tcx> { - fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { - match terminator.kind { - TerminatorKind::Drop { place, target, .. } => { - let ty = place.ty(self.body, self.tcx); - let needs_drop = ty.ty.needs_drop(self.tcx, self.param_env); - if !needs_drop { - self.optimizations.push((location, target)); - } - } - _ => {} - } - self.super_terminator(terminator, location); - } -} -pub struct RemoveUnneededDropsOptimizationFinder<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - optimizations: Vec<(Location, BasicBlock)>, - param_env: ParamEnv<'tcx>, -} diff --git a/compiler/rustc_mir/src/transform/rustc_peek.rs b/compiler/rustc_mir/src/transform/rustc_peek.rs index 205f718d6e..7598be4e4a 100644 --- a/compiler/rustc_mir/src/transform/rustc_peek.rs +++ b/compiler/rustc_mir/src/transform/rustc_peek.rs @@ -92,7 +92,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { /// "rustc_peek: bit not set". /// /// The intention is that one can write unit tests for dataflow by -/// putting code into a compile-fail test and using `rustc_peek` to +/// putting code into an UI test and using `rustc_peek` to /// make observations about the results of dataflow static analyses. /// /// (If there are any calls to `rustc_peek` that do not match the diff --git a/compiler/rustc_mir/src/transform/simplify.rs b/compiler/rustc_mir/src/transform/simplify.rs index b7c9a3a868..11539d3ef3 100644 --- a/compiler/rustc_mir/src/transform/simplify.rs +++ b/compiler/rustc_mir/src/transform/simplify.rs @@ -28,7 +28,6 @@ //! return. use crate::transform::MirPass; -use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -61,7 +60,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { } fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body); + debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body.source); simplify_cfg(body); } } @@ -288,17 +287,17 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { } pub fn remove_dead_blocks(body: &mut Body<'_>) { - let mut seen = BitSet::new_empty(body.basic_blocks().len()); - for (bb, _) in traversal::preorder(body) { - seen.insert(bb.index()); + let reachable = traversal::reachable_as_bitset(body); + let num_blocks = body.basic_blocks().len(); + if num_blocks == reachable.count() { + return; } let basic_blocks = body.basic_blocks_mut(); - - let num_blocks = basic_blocks.len(); let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); let mut used_blocks = 0; - for alive_index in seen.iter() { + for alive_index in reachable.iter() { + let alive_index = alive_index.index(); replacements[alive_index] = BasicBlock::new(used_blocks); if alive_index != used_blocks { // Swap the next alive block data with the current available slot. Since diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs index a3459887a9..05a8882807 100644 --- a/compiler/rustc_mir/src/transform/simplify_try.rs +++ b/compiler/rustc_mir/src/transform/simplify_try.rs @@ -113,7 +113,7 @@ fn get_arm_identity_info<'a, 'tcx>( test: impl Fn(&'a Statement<'tcx>) -> bool, mut action: impl FnMut(usize, &'a Statement<'tcx>), ) { - while stmt_iter.peek().map(|(_, stmt)| test(stmt)).unwrap_or(false) { + while stmt_iter.peek().map_or(false, |(_, stmt)| test(stmt)) { let (idx, stmt) = stmt_iter.next().unwrap(); action(idx, stmt); @@ -635,7 +635,7 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { }) .peekable(); - let bb_first = iter_bbs_reachable.peek().map(|(idx, _)| *idx).unwrap_or(&targets_and_values[0]); + let bb_first = iter_bbs_reachable.peek().map_or(&targets_and_values[0], |(idx, _)| *idx); let mut all_successors_equivalent = StatementEquality::TrivialEqual; // All successor basic blocks must be equal or contain statements that are pairwise considered equal. diff --git a/compiler/rustc_mir/src/util/alignment.rs b/compiler/rustc_mir/src/util/alignment.rs index a0728a6a63..f567c9cfaa 100644 --- a/compiler/rustc_mir/src/util/alignment.rs +++ b/compiler/rustc_mir/src/util/alignment.rs @@ -38,15 +38,12 @@ fn is_within_packed<'tcx, L>(tcx: TyCtxt<'tcx>, local_decls: &L, place: Place<'t where L: HasLocalDecls<'tcx>, { - let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in place.iter_projections().rev() { match elem { // encountered a Deref, which is ABI-aligned ProjectionElem::Deref => break, ProjectionElem::Field(..) => { - let ty = Place::ty_from(place.local, proj_base, local_decls, tcx).ty; + let ty = place_base.ty(local_decls, tcx).ty; match ty.kind() { ty::Adt(def, _) if def.repr.packed() => return true, _ => {} diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs index 89ce29bd10..7fc1c3a73a 100644 --- a/compiler/rustc_mir/src/util/pretty.rs +++ b/compiler/rustc_mir/src/util/pretty.rs @@ -273,8 +273,6 @@ pub fn write_mir_pretty<'tcx>( let mut first = true; for def_id in dump_mir_def_ids(tcx, single) { - let body = &tcx.optimized_mir(def_id); - if first { first = false; } else { @@ -282,11 +280,28 @@ pub fn write_mir_pretty<'tcx>( writeln!(w)?; } - write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?; - - for body in tcx.promoted_mir(def_id) { - writeln!(w)?; + let render_body = |w: &mut dyn Write, body| -> io::Result<()> { write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?; + + for body in tcx.promoted_mir(def_id) { + writeln!(w)?; + write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?; + } + Ok(()) + }; + match tcx.hir().body_const_context(def_id.expect_local()) { + None => render_body(w, tcx.optimized_mir(def_id))?, + // For `const fn` we want to render the optimized MIR. If you want the mir used in + // ctfe, you can dump the MIR after the `Deaggregator` optimization pass. + Some(rustc_hir::ConstContext::ConstFn) => { + render_body(w, tcx.optimized_mir(def_id))?; + writeln!(w)?; + writeln!(w, "// MIR FOR CTFE")?; + // Do not use `render_body`, as that would render the promoteds again, but these + // are shared between mir_for_ctfe and optimized_mir + write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w)?; + } + Some(_) => render_body(w, tcx.mir_for_ctfe(def_id))?, } } Ok(()) diff --git a/compiler/rustc_mir/src/util/storage.rs b/compiler/rustc_mir/src/util/storage.rs index 0b7b1c2953..4e1696cd71 100644 --- a/compiler/rustc_mir/src/util/storage.rs +++ b/compiler/rustc_mir/src/util/storage.rs @@ -1,6 +1,5 @@ use rustc_index::bit_set::BitSet; -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{self, Local, Location}; +use rustc_middle::mir::{self, Local}; /// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations. /// @@ -13,12 +12,18 @@ pub struct AlwaysLiveLocals(BitSet); impl AlwaysLiveLocals { pub fn new(body: &mir::Body<'tcx>) -> Self { - let mut ret = AlwaysLiveLocals(BitSet::new_filled(body.local_decls.len())); - - let mut vis = StorageAnnotationVisitor(&mut ret); - vis.visit_body(body); + let mut always_live_locals = AlwaysLiveLocals(BitSet::new_filled(body.local_decls.len())); + + for block in body.basic_blocks() { + for statement in &block.statements { + use mir::StatementKind::{StorageDead, StorageLive}; + if let StorageLive(l) | StorageDead(l) = statement.kind { + always_live_locals.0.remove(l); + } + } + } - ret + always_live_locals } pub fn into_inner(self) -> BitSet { @@ -33,15 +38,3 @@ impl std::ops::Deref for AlwaysLiveLocals { &self.0 } } - -/// Removes locals that have `Storage*` annotations from `AlwaysLiveLocals`. -struct StorageAnnotationVisitor<'a>(&'a mut AlwaysLiveLocals); - -impl Visitor<'tcx> for StorageAnnotationVisitor<'_> { - fn visit_statement(&mut self, statement: &mir::Statement<'tcx>, _location: Location) { - use mir::StatementKind::{StorageDead, StorageLive}; - if let StorageLive(l) | StorageDead(l) = statement.kind { - (self.0).0.remove(l); - } - } -} diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index e1a3dc87c8..3308a243a3 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -6,8 +6,8 @@ use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::thir::*; use rustc_hir::def_id::DefId; use rustc_hir::HirId; -use rustc_middle::middle::region; use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; +use rustc_middle::middle::region; use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::*; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance}; @@ -57,7 +57,8 @@ crate enum PlaceBase { /// DefId of the closure closure_def_id: DefId, /// The trait closure implements, `Fn`, `FnMut`, `FnOnce` - closure_kind: ty::ClosureKind }, + closure_kind: ty::ClosureKind, + }, } /// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a @@ -79,10 +80,9 @@ crate struct PlaceBuilder<'tcx> { /// part of a path that is captued by a closure. We stop applying projections once we see the first /// projection that isn't captured by a closure. fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( - mir_projections: &Vec>, + mir_projections: &[PlaceElem<'tcx>], ) -> Vec { - - let mut hir_projections = Vec::new(); + let mut hir_projections = Vec::new(); for mir_projection in mir_projections { let hir_projection = match mir_projection { @@ -91,20 +91,20 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( // We will never encouter this for multivariant enums, // read the comment for `Downcast`. HirProjectionKind::Field(field.index() as u32, VariantIdx::new(0)) - }, + } ProjectionElem::Downcast(..) => { // This projections exist only for enums that have // multiple variants. Since such enums that are captured // completely, we can stop here. - break - }, + break; + } ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { // We don't capture array-access projections. // We can stop here as arrays are captured completely. - break - }, + break; + } }; hir_projections.push(hir_projection); @@ -128,7 +128,7 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( /// list are being applied to the same root variable. fn is_ancestor_or_same_capture( proj_possible_ancestor: &Vec, - proj_capture: &Vec, + proj_capture: &[HirProjectionKind], ) -> bool { // We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false. // Therefore we can't just check if all projections are same in the zipped iterator below. @@ -171,7 +171,7 @@ fn find_capture_matching_projections<'a, 'tcx>( typeck_results: &'a ty::TypeckResults<'tcx>, var_hir_id: HirId, closure_def_id: DefId, - projections: &Vec>, + projections: &[PlaceElem<'tcx>], ) -> Option<(usize, &'a ty::CapturedPlace<'tcx>)> { let closure_min_captures = typeck_results.closure_min_captures.get(&closure_def_id)?; let root_variable_min_captures = closure_min_captures.get(&var_hir_id)?; @@ -181,9 +181,9 @@ fn find_capture_matching_projections<'a, 'tcx>( // If an ancestor is found, `idx` is the index within the list of captured places // for root variable `var_hir_id` and `capture` is the `ty::CapturedPlace` itself. let (idx, capture) = root_variable_min_captures.iter().enumerate().find(|(_, capture)| { - let possible_ancestor_proj_kinds = - capture.place.projections.iter().map(|proj| proj.kind).collect(); - is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections) + let possible_ancestor_proj_kinds = + capture.place.projections.iter().map(|proj| proj.kind).collect(); + is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections) })?; // Convert index to be from the presepective of the entire closure_min_captures map @@ -213,35 +213,34 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( ty::ClosureKind::FnOnce => {} } - let (capture_index, capture) = - if let Some(capture_details) = find_capture_matching_projections( + let (capture_index, capture) = if let Some(capture_details) = + find_capture_matching_projections( typeck_results, var_hir_id, closure_def_id, &from_builder.projection, ) { - capture_details - } else { - if !tcx.features().capture_disjoint_fields { - bug!( - "No associated capture found for {:?}[{:#?}] even though \ + capture_details + } else { + if !tcx.features().capture_disjoint_fields { + bug!( + "No associated capture found for {:?}[{:#?}] even though \ capture_disjoint_fields isn't enabled", - var_hir_id, - from_builder.projection - ) - } else { - // FIXME(project-rfc-2229#24): Handle this case properly - debug!( - "No associated capture found for {:?}[{:#?}]", - var_hir_id, - from_builder.projection, - ); - } - return Err(var_hir_id); - }; + var_hir_id, + from_builder.projection + ) + } else { + // FIXME(project-rfc-2229#24): Handle this case properly + debug!( + "No associated capture found for {:?}[{:#?}]", + var_hir_id, from_builder.projection, + ); + } + return Err(var_hir_id); + }; - let closure_ty = - typeck_results.node_type(tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local())); + let closure_ty = typeck_results + .node_type(tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local())); let substs = match closure_ty.kind() { ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), @@ -256,7 +255,8 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( // we know that the capture exists and is the `capture_index`-th capture. let var_ty = substs.tupled_upvars_ty().tuple_element_ty(capture_index).unwrap(); - upvar_resolved_place_builder = upvar_resolved_place_builder.field(Field::new(capture_index), var_ty); + upvar_resolved_place_builder = + upvar_resolved_place_builder.field(Field::new(capture_index), var_ty); // If the variable is captured via ByRef(Immutable/Mutable) Borrow, // we need to deref it @@ -270,8 +270,9 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( // We used some of the projections to build the capture itself, // now we apply the remaining to the upvar resolved place. - upvar_resolved_place_builder.projection.extend( - curr_projections.drain(next_projection..)); + upvar_resolved_place_builder + .projection + .extend(curr_projections.drain(next_projection..)); Ok(upvar_resolved_place_builder) } @@ -303,7 +304,7 @@ impl<'tcx> PlaceBuilder<'tcx> { self.base } - fn field(self, f: Field, ty: Ty<'tcx>) -> Self { + crate fn field(self, f: Field, ty: Ty<'tcx>) -> Self { self.project(PlaceElem::Field(f, ty)) } @@ -356,7 +357,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// This is used when constructing a compound `Place`, so that we can avoid creating /// intermediate `Place` values until we know the full set of projections. - crate fn as_place_builder(&mut self, block: BasicBlock, expr: M) -> BlockAnd> + crate fn as_place_builder( + &mut self, + block: BasicBlock, + expr: M, + ) -> BlockAnd> where M: Mirror<'tcx, Output = Expr<'tcx>>, { @@ -531,6 +536,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Borrow { .. } | ExprKind::AddressOf { .. } | ExprKind::Match { .. } + | ExprKind::If { .. } | ExprKind::Loop { .. } | ExprKind::Block { .. } | ExprKind::Assign { .. } @@ -626,7 +632,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if is_outermost_index { self.read_fake_borrows(block, fake_borrow_temps, source_info) } else { - base_place = base_place.expect_upvars_resolved(self.hir.tcx(), self.hir.typeck_results()); + base_place = + base_place.expect_upvars_resolved(self.hir.tcx(), self.hir.typeck_results()); self.add_fake_borrows_of_base( &base_place, block, @@ -678,7 +685,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tcx = self.hir.tcx(); let local = match base_place.base { PlaceBase::Local(local) => local, - PlaceBase::Upvar { .. } => bug!("Expected PlacseBase::Local found Upvar") + PlaceBase::Upvar { .. } => bug!("Expected PlacseBase::Local found Upvar"), }; let place_ty = Place::ty_from(local, &base_place.projection, &self.local_decls, tcx); diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 581d842142..e602f4dd71 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -2,9 +2,9 @@ use rustc_index::vec::Idx; +use crate::build::expr::as_place::PlaceBase; use crate::build::expr::category::{Category, RvalueFunc}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; -use crate::build::expr::as_place::PlaceBase; use crate::thir::*; use rustc_middle::middle::region; use rustc_middle::mir::AssertKind; @@ -239,6 +239,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::StaticRef { .. } | ExprKind::Block { .. } | ExprKind::Match { .. } + | ExprKind::If { .. } | ExprKind::NeverToAny { .. } | ExprKind::Use { .. } | ExprKind::Borrow { .. } @@ -261,7 +262,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ValueTypeAscription { .. } => { // these do not have corresponding `Rvalue` variants, // so make an operand and then return that - debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Rvalue(RvalueFunc::AsRvalue)))); + debug_assert!(!matches!( + Category::of(&expr.kind), + Some(Category::Rvalue(RvalueFunc::AsRvalue)) + )); let operand = unpack!(block = this.as_operand(block, scope, expr)); block.and(Rvalue::Use(operand)) } @@ -388,34 +392,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // We are capturing a path that starts off a local variable in the parent. // The mutability of the current capture is same as the mutability // of the local declaration in the parent. - PlaceBase::Local(local) => this.local_decls[local].mutability, + PlaceBase::Local(local) => this.local_decls[local].mutability, // Parent is a closure and we are capturing a path that is captured // by the parent itself. The mutability of the current capture // is same as that of the capture in the parent closure. PlaceBase::Upvar { .. } => { - let enclosing_upvars_resolved = arg_place_builder.clone().into_place( - this.hir.tcx(), - this.hir.typeck_results()); + let enclosing_upvars_resolved = + arg_place_builder.clone().into_place(this.hir.tcx(), this.hir.typeck_results()); match enclosing_upvars_resolved.as_ref() { - PlaceRef { local, projection: &[ProjectionElem::Field(upvar_index, _), ..] } + PlaceRef { + local, + projection: &[ProjectionElem::Field(upvar_index, _), ..], + } | PlaceRef { local, - projection: &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..] } => { - // Not in a closure - debug_assert!( - local == Local::new(1), - "Expected local to be Local(1), found {:?}", - local - ); - // Not in a closure - debug_assert!( - this.upvar_mutbls.len() > upvar_index.index(), - "Unexpected capture place, upvar_mutbls={:#?}, upvar_index={:?}", - this.upvar_mutbls, upvar_index - ); - this.upvar_mutbls[upvar_index.index()] - } + projection: + &[ProjectionElem::Deref, ProjectionElem::Field(upvar_index, _), ..], + } => { + // Not in a closure + debug_assert!( + local == Local::new(1), + "Expected local to be Local(1), found {:?}", + local + ); + // Not in a closure + debug_assert!( + this.upvar_mutbls.len() > upvar_index.index(), + "Unexpected capture place, upvar_mutbls={:#?}, upvar_index={:?}", + this.upvar_mutbls, + upvar_index + ); + this.upvar_mutbls[upvar_index.index()] + } _ => bug!("Unexpected capture place"), } } @@ -426,9 +435,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, }; - let arg_place = arg_place_builder.into_place( - this.hir.tcx(), - this.hir.typeck_results()); + let arg_place = arg_place_builder.into_place(this.hir.tcx(), this.hir.typeck_results()); this.cfg.push_assign( block, diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs index 8561170856..9320b5810e 100644 --- a/compiler/rustc_mir_build/src/build/expr/category.rs +++ b/compiler/rustc_mir_build/src/build/expr/category.rs @@ -45,6 +45,7 @@ impl Category { ExprKind::LogicalOp { .. } | ExprKind::Match { .. } + | ExprKind::If { .. } | ExprKind::NeverToAny { .. } | ExprKind::Use { .. } | ExprKind::Adt { .. } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index a86e4cb99c..235fe14cbf 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -8,9 +8,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_middle::mir::*; -use rustc_middle::ty::{self, CanonicalUserTypeAnnotation}; -use rustc_span::symbol::sym; -use rustc_target::spec::abi::Abi; +use rustc_middle::ty::CanonicalUserTypeAnnotation; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, storing the result into `destination`, which @@ -30,11 +28,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let expr_span = expr.span; let source_info = this.source_info(expr_span); - let expr_is_block_or_scope = match expr.kind { - ExprKind::Block { .. } => true, - ExprKind::Scope { .. } => true, - _ => false, - }; + let expr_is_block_or_scope = + matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. }); if !expr_is_block_or_scope { this.block_context.push(BlockFrame::SubExpr); @@ -55,13 +50,52 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Match { scrutinee, arms } => { this.match_expr(destination, expr_span, block, scrutinee, arms) } + ExprKind::If { cond, then, else_opt } => { + let place = unpack!( + block = this.as_temp(block, Some(this.local_scope()), cond, Mutability::Mut) + ); + let operand = Operand::Move(Place::from(place)); + + let mut then_block = this.cfg.start_new_block(); + let mut else_block = this.cfg.start_new_block(); + let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block); + this.cfg.terminate(block, source_info, term); + + unpack!(then_block = this.into(destination, then_block, then)); + else_block = if let Some(else_opt) = else_opt { + unpack!(this.into(destination, else_block, else_opt)) + } else { + // Body of the `if` expression without an `else` clause must return `()`, thus + // we implicitly generate a `else {}` if it is not specified. + let correct_si = this.source_info(expr_span.shrink_to_hi()); + this.cfg.push_assign_unit(else_block, correct_si, destination, this.hir.tcx()); + else_block + }; + + let join_block = this.cfg.start_new_block(); + this.cfg.terminate( + then_block, + source_info, + TerminatorKind::Goto { target: join_block }, + ); + this.cfg.terminate( + else_block, + source_info, + TerminatorKind::Goto { target: join_block }, + ); + + join_block.unit() + } ExprKind::NeverToAny { source } => { let source = this.hir.mirror(source); - let is_call = matches!(source.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. }); + let is_call = + matches!(source.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. }); // (#66975) Source could be a const of type `!`, so has to // exist in the generated MIR. - unpack!(block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut,)); + unpack!( + block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut,) + ); // This is an optimization. If the expression was a call then we already have an // unreachable block. Don't bother to terminate it and create a new one. @@ -161,78 +195,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None }) } - ExprKind::Call { ty, fun, args, from_hir_call, fn_span } => { - let intrinsic = match *ty.kind() { - ty::FnDef(def_id, _) => { - let f = ty.fn_sig(this.hir.tcx()); - if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { - Some(this.hir.tcx().item_name(def_id)) - } else { - None - } - } - _ => None, - }; + ExprKind::Call { ty: _, fun, args, from_hir_call, fn_span } => { let fun = unpack!(block = this.as_local_operand(block, fun)); - if let Some(sym::move_val_init) = intrinsic { - // `move_val_init` has "magic" semantics - the second argument is - // always evaluated "directly" into the first one. - - let mut args = args.into_iter(); - let ptr = args.next().expect("0 arguments to `move_val_init`"); - let val = args.next().expect("1 argument to `move_val_init`"); - assert!(args.next().is_none(), ">2 arguments to `move_val_init`"); - - let ptr = this.hir.mirror(ptr); - let ptr_ty = ptr.ty; - // Create an *internal* temp for the pointer, so that unsafety - // checking won't complain about the raw pointer assignment. - let ptr_temp = this - .local_decls - .push(LocalDecl::with_source_info(ptr_ty, source_info).internal()); - let ptr_temp = Place::from(ptr_temp); - // No need for a scope, ptr_temp doesn't need drop - let block = unpack!(this.into(ptr_temp, block, ptr)); - // Maybe we should provide a scope here so that - // `move_val_init` wouldn't leak on panic even with an - // arbitrary `val` expression, but `schedule_drop`, - // borrowck and drop elaboration all prevent us from - // dropping `ptr_temp.deref()`. - this.into(this.hir.tcx().mk_place_deref(ptr_temp), block, val) - } else { - let args: Vec<_> = args - .into_iter() - .map(|arg| unpack!(block = this.as_local_call_operand(block, arg))) - .collect(); + let args: Vec<_> = args + .into_iter() + .map(|arg| unpack!(block = this.as_local_call_operand(block, arg))) + .collect(); - let success = this.cfg.start_new_block(); + let success = this.cfg.start_new_block(); - this.record_operands_moved(&args); + this.record_operands_moved(&args); - debug!("into_expr: fn_span={:?}", fn_span); + debug!("into_expr: fn_span={:?}", fn_span); - this.cfg.terminate( - block, - source_info, - TerminatorKind::Call { - func: fun, - args, - cleanup: None, - // FIXME(varkor): replace this with an uninhabitedness-based check. - // This requires getting access to the current module to call - // `tcx.is_ty_uninhabited_from`, which is currently tricky to do. - destination: if expr.ty.is_never() { - None - } else { - Some((destination, success)) - }, - from_hir_call, - fn_span, + this.cfg.terminate( + block, + source_info, + TerminatorKind::Call { + func: fun, + args, + cleanup: None, + // FIXME(varkor): replace this with an uninhabitedness-based check. + // This requires getting access to the current module to call + // `tcx.is_ty_uninhabited_from`, which is currently tricky to do. + destination: if expr.ty.is_never() { + None + } else { + Some((destination, success)) }, - ); - this.diverge_from(block); - success.unit() - } + from_hir_call, + fn_span, + }, + ); + this.diverge_from(block); + success.unit() } ExprKind::Use { source } => this.into(destination, block, source), ExprKind::Borrow { arg, borrow_kind } => { @@ -277,7 +273,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let field_names = this.hir.all_fields(adt_def, variant_index); let fields: Vec<_> = if let Some(FruInfo { base, field_types }) = base { - let base = unpack!(block = this.as_place(block, base)); + let place_builder = unpack!(block = this.as_place_builder(block, base)); // MIR does not natively support FRU, so for each // base-supplied field, generate an operand that @@ -287,9 +283,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .zip(field_types.into_iter()) .map(|(n, ty)| match fields_map.get(&n) { Some(v) => v.clone(), - None => this.consume_by_copy_or_move( - this.hir.tcx().mk_place_field(base, n, ty), - ), + None => { + let place_builder = place_builder.clone(); + this.consume_by_copy_or_move( + place_builder + .field(n, ty) + .into_place(this.hir.tcx(), this.hir.typeck_results()), + ) + } }) .collect() } else { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index c5f9412bf0..fde007ec01 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -82,8 +82,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// visible through borrow checking. False edges ensure that the CFG as /// seen by borrow checking doesn't encode this. False edges are added: /// - /// * From each prebinding block to the next prebinding block. - /// * From each otherwise block to the next prebinding block. + /// * From each pre-binding block to the next pre-binding block. + /// * From each otherwise block to the next pre-binding block. crate fn match_expr( &mut self, destination: Place<'tcx>, @@ -690,10 +690,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { #[derive(Debug)] struct Candidate<'pat, 'tcx> { - /// `Span` of the original pattern that gave rise to this candidate + /// [`Span`] of the original pattern that gave rise to this candidate. span: Span, - /// This `Candidate` has a guard. + /// Whether this `Candidate` has a guard. has_guard: bool, /// All of these must be satisfied... @@ -705,14 +705,15 @@ struct Candidate<'pat, 'tcx> { /// ...and these types asserted... ascriptions: Vec>, - /// ... and if this is non-empty, one of these subcandidates also has to match ... + /// ...and if this is non-empty, one of these subcandidates also has to match... subcandidates: Vec>, - /// ...and the guard must be evaluated, if false branch to Block... + /// ...and the guard must be evaluated; if it's `false` then branch to `otherwise_block`. otherwise_block: Option, - /// ...and the blocks for add false edges between candidates + /// The block before the `bindings` have been established. pre_binding_block: Option, + /// The pre-binding block of the next candidate. next_candidate_pre_binding_block: Option, } @@ -797,18 +798,19 @@ crate struct MatchPair<'pat, 'tcx> { pattern: &'pat Pat<'tcx>, } +/// See [`Test`] for more. #[derive(Clone, Debug, PartialEq)] enum TestKind<'tcx> { - /// Test the branches of enum. + /// Test what enum variant a value is. Switch { - /// The enum being tested + /// The enum type being tested. adt_def: &'tcx ty::AdtDef, /// The set of variants that we should create a branch for. We also /// create an additional "otherwise" case. variants: BitSet, }, - /// Test what value an `integer`, `bool` or `char` has. + /// Test what value an integer, `bool`, or `char` has. SwitchInt { /// The type of the value that we're testing. switch_ty: Ty<'tcx>, @@ -816,7 +818,7 @@ enum TestKind<'tcx> { /// /// For integers and `char`s we create a branch to each of the values in /// `options`, as well as an "otherwise" branch for all other values, even - /// in the (rare) case that options is exhaustive. + /// in the (rare) case that `options` is exhaustive. /// /// For `bool` we always generate two edges, one for `true` and one for /// `false`. @@ -836,17 +838,21 @@ enum TestKind<'tcx> { /// Test whether the value falls within an inclusive or exclusive range Range(PatRange<'tcx>), - /// Test length of the slice is equal to len + /// Test that the length of the slice is equal to `len`. Len { len: u64, op: BinOp }, } +/// A test to perform to determine which [`Candidate`] matches a value. +/// +/// [`Test`] is just the test to perform; it does not include the value +/// to be tested. #[derive(Debug)] crate struct Test<'tcx> { span: Span, kind: TestKind<'tcx>, } -/// ArmHasGuard is isomorphic to a boolean flag. It indicates whether +/// `ArmHasGuard` is a wrapper around a boolean flag. It indicates whether /// a match arm has a guard expression attached to it. #[derive(Copy, Clone, Debug)] crate struct ArmHasGuard(crate bool); @@ -861,27 +867,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// candidates are sorted such that the first item in the list /// has the highest priority. When a candidate is found to match /// the value, we will set and generate a branch to the appropriate - /// prebinding block. + /// pre-binding block. /// /// If we find that *NONE* of the candidates apply, we branch to the /// `otherwise_block`, setting it to `Some` if required. In principle, this /// means that the input list was not exhaustive, though at present we /// sometimes are not smart enough to recognize all exhaustive inputs. /// - /// It might be surprising that the input can be inexhaustive. + /// It might be surprising that the input can be non-exhaustive. /// Indeed, initially, it is not, because all matches are /// exhaustive in Rust. But during processing we sometimes divide /// up the list of candidates and recurse with a non-exhaustive /// list. This is important to keep the size of the generated code - /// under control. See `test_candidates` for more details. + /// under control. See [`Builder::test_candidates`] for more details. /// - /// If `fake_borrows` is Some, then places which need fake borrows + /// If `fake_borrows` is `Some`, then places which need fake borrows /// will be added to it. /// /// For an example of a case where we set `otherwise_block`, even for an - /// exhaustive match consider: + /// exhaustive match, consider: /// - /// ```rust + /// ``` /// match x { /// (true, true) => (), /// (_, false) => (), @@ -890,8 +896,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// ``` /// /// For this match, we check if `x.0` matches `true` (for the first - /// arm). If that's false, we check `x.1`. If it's `true` we check if - /// `x.0` matches `false` (for the third arm). In the (impossible at + /// arm). If it doesn't match, we check `x.1`. If `x.1` is `true` we check + /// if `x.0` matches `false` (for the third arm). In the (impossible at /// runtime) case when `x.0` is now `true`, we branch to /// `otherwise_block`. fn match_candidates<'pat>( @@ -998,26 +1004,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } - /// Link up matched candidates. For example, if we have something like - /// this: + /// Link up matched candidates. + /// + /// For example, if we have something like this: /// /// ```rust /// ... - /// Some(x) if cond => ... + /// Some(x) if cond1 => ... /// Some(x) => ... - /// Some(x) if cond => ... + /// Some(x) if cond2 => ... /// ... /// ``` /// /// We generate real edges from: - /// * `start_block` to the `prebinding_block` of the first pattern, - /// * the otherwise block of the first pattern to the second pattern, - /// * the otherwise block of the third pattern to the a block with an - /// Unreachable terminator. /// - /// As well as that we add fake edges from the otherwise blocks to the - /// prebinding block of the next candidate in the original set of + /// * `start_block` to the [pre-binding block] of the first pattern, + /// * the [otherwise block] of the first pattern to the second pattern, + /// * the [otherwise block] of the third pattern to a block with an + /// [`Unreachable` terminator](TerminatorKind::Unreachable). + /// + /// In addition, we add fake edges from the otherwise blocks to the + /// pre-binding block of the next candidate in the original set of /// candidates. + /// + /// [pre-binding block]: Candidate::pre_binding_block + /// [otherwise block]: Candidate::otherwise_block fn select_matched_candidates( &mut self, matched_candidates: &mut [&mut Candidate<'_, 'tcx>], @@ -1104,7 +1115,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// forwards to [Builder::test_candidates]. /// /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like - /// so + /// so: /// /// ```text /// [ start ] @@ -1274,10 +1285,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// This is the most subtle part of the matching algorithm. At /// this point, the input candidates have been fully simplified, /// and so we know that all remaining match-pairs require some - /// sort of test. To decide what test to do, we take the highest - /// priority candidate (last one in the list) and extract the - /// first match-pair from the list. From this we decide what kind - /// of test is needed using `test`, defined in the `test` module. + /// sort of test. To decide what test to perform, we take the highest + /// priority candidate (the first one in the list, as of January 2021) + /// and extract the first match-pair from the list. From this we decide + /// what kind of test is needed using [`Builder::test`], defined in the + /// [`test` module](mod@test). /// /// *Note:* taking the first match pair is somewhat arbitrary, and /// we might do better here by choosing more carefully what to @@ -1285,20 +1297,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// For example, consider the following possible match-pairs: /// - /// 1. `x @ Some(P)` -- we will do a `Switch` to decide what variant `x` has - /// 2. `x @ 22` -- we will do a `SwitchInt` - /// 3. `x @ 3..5` -- we will do a range test + /// 1. `x @ Some(P)` -- we will do a [`Switch`] to decide what variant `x` has + /// 2. `x @ 22` -- we will do a [`SwitchInt`] to decide what value `x` has + /// 3. `x @ 3..5` -- we will do a [`Range`] test to decide what range `x` falls in /// 4. etc. /// + /// [`Switch`]: TestKind::Switch + /// [`SwitchInt`]: TestKind::SwitchInt + /// [`Range`]: TestKind::Range + /// /// Once we know what sort of test we are going to perform, this - /// Tests may also help us with other candidates. So we walk over + /// test may also help us winnow down our candidates. So we walk over /// the candidates (from high to low priority) and check. This /// gives us, for each outcome of the test, a transformed list of - /// candidates. For example, if we are testing the current - /// variant of `x.0`, and we have a candidate `{x.0 @ Some(v), x.1 - /// @ 22}`, then we would have a resulting candidate of `{(x.0 as - /// Some).0 @ v, x.1 @ 22}`. Note that the first match-pair is now - /// simpler (and, in fact, irrefutable). + /// candidates. For example, if we are testing `x.0`'s variant, + /// and we have a candidate `(x.0 @ Some(v), x.1 @ 22)`, + /// then we would have a resulting candidate of `((x.0 as Some).0 @ v, x.1 @ 22)`. + /// Note that the first match-pair is now simpler (and, in fact, irrefutable). /// /// But there may also be candidates that the test just doesn't /// apply to. The classical example involves wildcards: @@ -1328,7 +1343,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// is trivially NP-complete: /// /// ```rust - /// match (var0, var1, var2, var3, ..) { + /// match (var0, var1, var2, var3, ...) { /// (true, _, _, false, true, ...) => false, /// (_, true, true, false, _, ...) => false, /// (false, _, false, false, _, ...) => false, @@ -1343,7 +1358,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// That kind of exponential worst-case might not occur in practice, but /// our simplistic treatment of constants and guards would make it occur - /// in very common situations - for example #29740: + /// in very common situations - for example [#29740]: /// /// ```rust /// match x { @@ -1354,13 +1369,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// } /// ``` /// - /// Here we first test the match-pair `x @ "foo"`, which is an `Eq` test. + /// [#29740]: https://github.com/rust-lang/rust/issues/29740 + /// + /// Here we first test the match-pair `x @ "foo"`, which is an [`Eq` test]. + /// + /// [`Eq` test]: TestKind::Eq /// /// It might seem that we would end up with 2 disjoint candidate - /// sets, consisting of the first candidate or the other 3, but our - /// algorithm doesn't reason about "foo" being distinct from the other + /// sets, consisting of the first candidate or the other two, but our + /// algorithm doesn't reason about `"foo"` being distinct from the other /// constants; it considers the latter arms to potentially match after - /// both outcomes, which obviously leads to an exponential amount + /// both outcomes, which obviously leads to an exponential number /// of tests. /// /// To avoid these kinds of problems, our algorithm tries to ensure @@ -1372,16 +1391,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// After we perform our test, we branch into the appropriate candidate /// set and recurse with `match_candidates`. These sub-matches are - /// obviously inexhaustive - as we discarded our otherwise set - so + /// obviously non-exhaustive - as we discarded our otherwise set - so /// we set their continuation to do `match_candidates` on the - /// "unmatched" set (which is again inexhaustive). + /// "unmatched" set (which is again non-exhaustive). /// /// If you apply this to the above test, you basically wind up /// with an if-else-if chain, testing each candidate in turn, /// which is precisely what we want. /// /// In addition to avoiding exponential-time blowups, this algorithm - /// also has nice property that each guard and arm is only generated + /// also has the nice property that each guard and arm is only generated /// once. fn test_candidates<'pat, 'b, 'c>( &mut self, @@ -1733,15 +1752,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let e = self.hir.mirror(e.clone()); let source_info = self.source_info(e.span); (e.span, self.test_bool(block, e, source_info)) - }, + } Guard::IfLet(pat, scrutinee) => { let scrutinee_span = scrutinee.span(); - let scrutinee_place = unpack!(block = self.lower_scrutinee(block, scrutinee.clone(), scrutinee_span)); + let scrutinee_place = unpack!( + block = self.lower_scrutinee(block, scrutinee.clone(), scrutinee_span) + ); let mut guard_candidate = Candidate::new(scrutinee_place, &pat, false); let wildcard = Pat::wildcard_from_ty(pat.ty); let mut otherwise_candidate = Candidate::new(scrutinee_place, &wildcard, false); - let fake_borrow_temps = - self.lower_match_tree(block, pat.span, false, &mut [&mut guard_candidate, &mut otherwise_candidate]); + let fake_borrow_temps = self.lower_match_tree( + block, + pat.span, + false, + &mut [&mut guard_candidate, &mut otherwise_candidate], + ); self.declare_bindings( None, pat.span.to(arm_span.unwrap()), diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 705266d4a0..ddfaeafc07 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -15,7 +15,6 @@ use crate::build::matches::{Ascription, Binding, Candidate, MatchPair}; use crate::build::Builder; use crate::thir::{self, *}; -use rustc_attr::{SignedInt, UnsignedInt}; use rustc_hir::RangeEnd; use rustc_middle::mir::Place; use rustc_middle::ty; @@ -55,7 +54,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // * the bindings from the previous iteration of the loop is prepended to the bindings from // the current iteration (in the implementation this is done by mem::swap and extend) // * after all iterations, these new bindings are then appended to the bindings that were - // prexisting (i.e. `candidate.binding` when the function was called). + // preexisting (i.e. `candidate.binding` when the function was called). // // example: // candidate.bindings = [1, 2, 3] @@ -203,13 +202,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0) } ty::Int(ity) => { - let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); + let size = Integer::from_int_ty(&tcx, ity).size(); let max = size.truncate(u128::MAX); let bias = 1u128 << (size.bits() - 1); (Some((0, max, size)), bias) } ty::Uint(uty) => { - let size = Integer::from_attr(&tcx, UnsignedInt(uty)).size(); + let size = Integer::from_uint_ty(&tcx, uty).size(); let max = size.truncate(u128::MAX); (Some((0, max, size)), 0) } diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 07173f41cd..126fb957a6 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -23,7 +23,7 @@ use std::cmp::Ordering; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Identifies what test is needed to decide if `match_pair` is applicable. /// - /// It is a bug to call this with a simplifiable pattern. + /// It is a bug to call this with a not-fully-simplified pattern. pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { match *match_pair.pattern.kind { PatKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => Test { diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 4ef88c25ca..db1f678a5c 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -32,9 +32,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) { let tcx = self.hir.tcx(); let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind() { - ty::Array(_, length) => { - (length.eval_usize(tcx, self.hir.param_env), true) - } + ty::Array(_, length) => (length.eval_usize(tcx, self.hir.param_env), true), _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), }; diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index a207997f57..5f6c8d2640 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -830,9 +830,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty), }; let capture_tys = upvar_substs.upvar_tys(); - let captures_with_tys = hir_typeck_results - .closure_min_captures_flattened(fn_def_id) - .zip(capture_tys); + let captures_with_tys = + hir_typeck_results.closure_min_captures_flattened(fn_def_id).zip(capture_tys); self.upvar_mutbls = captures_with_tys .enumerate() @@ -840,25 +839,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let capture = captured_place.info.capture_kind; let var_id = match captured_place.place.base { HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, - _ => bug!("Expected an upvar") + _ => bug!("Expected an upvar"), }; - let mut mutability = Mutability::Not; + let mutability = captured_place.mutability; // FIXME(project-rfc-2229#8): Store more precise information - let mut name = kw::Invalid; + let mut name = kw::Empty; if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) { if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { name = ident.name; - match hir_typeck_results - .extract_binding_mode(tcx.sess, pat.hir_id, pat.span) - { - Some(ty::BindByValue(hir::Mutability::Mut)) => { - mutability = Mutability::Mut; - } - Some(_) => mutability = Mutability::Not, - _ => {} - } } } diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index e76175c045..5e9d780d17 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -892,10 +892,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local_scope = self.local_scope(); let scope = self.scopes.scopes.last_mut().unwrap(); - assert_eq!( - scope.region_scope, local_scope, - "local scope is not the topmost scope!", - ); + assert_eq!(scope.region_scope, local_scope, "local scope is not the topmost scope!",); // look for moves of a local variable, like `MOVE(_X)` let locals_moved = operands.iter().flat_map(|operand| match operand { @@ -1008,9 +1005,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { matches!( self.cfg.block_data(start).terminator().kind, TerminatorKind::Assert { .. } - | TerminatorKind::Call {..} - | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::FalseUnwind { .. } ), "diverge_from called on block with terminator that cannot unwind." ); diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index dfe82317f4..969f7d1e3a 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -39,7 +39,7 @@ crate fn lit_to_const<'tcx>( let id = tcx.allocate_bytes(data); ConstValue::Scalar(Scalar::Ptr(id.into())) } - (ast::LitKind::Byte(n), ty::Uint(ast::UintTy::U8)) => { + (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) } (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { @@ -56,11 +56,11 @@ crate fn lit_to_const<'tcx>( Ok(ty::Const::from_value(tcx, lit, ty)) } -fn parse_float<'tcx>(num: Symbol, fty: ast::FloatTy, neg: bool) -> Result, ()> { +fn parse_float<'tcx>(num: Symbol, fty: ty::FloatTy, neg: bool) -> Result, ()> { let num = num.as_str(); use rustc_apfloat::ieee::{Double, Single}; let scalar = match fty { - ast::FloatTy::F32 => { + ty::FloatTy::F32 => { num.parse::().map_err(|_| ())?; let mut f = num.parse::().unwrap_or_else(|e| { panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e) @@ -70,7 +70,7 @@ fn parse_float<'tcx>(num: Symbol, fty: ast::FloatTy, neg: bool) -> Result { + ty::FloatTy::F64 => { num.parse::().map_err(|_| ())?; let mut f = num.parse::().unwrap_or_else(|e| { panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e) diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 417f9bded0..2962cbe815 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -537,13 +537,16 @@ fn make_mirror_unadjusted<'a, 'tcx>( }, Err(err) => bug!("invalid loop id for continue: {}", err), }, + hir::ExprKind::If(cond, then, else_opt) => ExprKind::If { + cond: cond.to_ref(), + then: then.to_ref(), + else_opt: else_opt.map(|el| el.to_ref()), + }, hir::ExprKind::Match(ref discr, ref arms, _) => ExprKind::Match { scrutinee: discr.to_ref(), arms: arms.iter().map(|a| convert_arm(cx, a)).collect(), }, - hir::ExprKind::Loop(ref body, _, _) => { - ExprKind::Loop { body: block::to_expr_ref(cx, body) } - } + hir::ExprKind::Loop(ref body, ..) => ExprKind::Loop { body: block::to_expr_ref(cx, body) }, hir::ExprKind::Field(ref source, ..) => ExprKind::Field { lhs: source.to_ref(), name: Field::new(cx.tcx.field_index(expr.hir_id, cx.typeck_results)), @@ -813,8 +816,7 @@ fn convert_path_expr<'a, 'tcx>( let item_id = cx.tcx.hir().get_parent_node(hir_id); let item_def_id = cx.tcx.hir().local_def_id(item_id); let generics = cx.tcx.generics_of(item_def_id); - let local_def_id = cx.tcx.hir().local_def_id(hir_id); - let index = generics.param_def_id_to_index[&local_def_id.to_def_id()]; + let index = generics.param_def_id_to_index[&def_id]; let name = cx.tcx.hir().name(hir_id); let val = ty::ConstKind::Param(ty::ParamConst::new(index, name)); ExprKind::Literal { diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs index ace9cad4d2..ed3d392782 100644 --- a/compiler/rustc_mir_build/src/thir/mod.rs +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -139,6 +139,11 @@ crate enum ExprKind<'tcx> { Box { value: ExprRef<'tcx>, }, + If { + cond: ExprRef<'tcx>, + then: ExprRef<'tcx>, + else_opt: Option>, + }, Call { ty: Ty<'tcx>, fun: ExprRef<'tcx>, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index db817b378f..397706851c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -403,7 +403,7 @@ fn report_arm_reachability<'p, 'tcx>( match is_useful { NotUseful => { match source { - hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(), + hir::MatchSource::WhileDesugar => bug!(), hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => { // Check which arm we're on. @@ -503,6 +503,11 @@ fn non_exhaustive_match<'p, 'tcx>( )); } } + if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() { + if cx.tcx.is_ty_uninhabited_from(cx.module, sub_ty, cx.param_env) { + err.note("references are always considered inhabited"); + } + } err.emit(); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index d79dd97a69..e67166c99c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -52,7 +52,6 @@ use super::{FieldPat, Pat, PatKind, PatRange}; use rustc_data_structures::captures::Captures; use rustc_index::vec::Idx; -use rustc_attr::{SignedInt, UnsignedInt}; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, RangeEnd}; use rustc_middle::mir::interpret::ConstValue; @@ -103,10 +102,10 @@ impl IntRange { ty::Bool => Some((Size::from_bytes(1), 0)), ty::Char => Some((Size::from_bytes(4), 0)), ty::Int(ity) => { - let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); + let size = Integer::from_int_ty(&tcx, ity).size(); Some((size, 1u128 << (size.bits() as u128 - 1))) } - ty::Uint(uty) => Some((Integer::from_attr(&tcx, UnsignedInt(uty)).size(), 0)), + ty::Uint(uty) => Some((Integer::from_uint_ty(&tcx, uty).size(), 0)), _ => None, } } @@ -167,7 +166,7 @@ impl IntRange { fn signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> u128 { match *ty.kind() { ty::Int(ity) => { - let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128; + let bits = Integer::from_int_ty(&tcx, ity).size().bits() as u128; 1u128 << (bits - 1) } _ => 0, @@ -328,8 +327,8 @@ struct SplitIntRange { } impl SplitIntRange { - fn new(r: IntRange) -> Self { - SplitIntRange { range: r.clone(), borders: Vec::new() } + fn new(range: IntRange) -> Self { + SplitIntRange { range, borders: Vec::new() } } /// Internal use @@ -959,13 +958,13 @@ impl<'tcx> SplitWildcard<'tcx> { smallvec![NonExhaustive] } &ty::Int(ity) => { - let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; + let bits = Integer::from_int_ty(&cx.tcx, ity).size().bits() as u128; let min = 1u128 << (bits - 1); let max = min - 1; smallvec![make_range(min, max)] } &ty::Uint(uty) => { - let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); + let size = Integer::from_uint_ty(&cx.tcx, uty).size(); let max = size.truncate(u128::MAX); smallvec![make_range(0, max)] } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 7e9a3a3727..7186e26be8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -9,7 +9,6 @@ pub(crate) use self::check_match::check_match; use crate::thir::util::UserAnnotatedTyHelpers; -use rustc_ast as ast; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -1069,20 +1068,19 @@ crate fn compare_const_vals<'tcx>( if let (Some(a), Some(b)) = (a_bits, b_bits) { use rustc_apfloat::Float; return match *ty.kind() { - ty::Float(ast::FloatTy::F32) => { + ty::Float(ty::FloatTy::F32) => { let l = rustc_apfloat::ieee::Single::from_bits(a); let r = rustc_apfloat::ieee::Single::from_bits(b); l.partial_cmp(&r) } - ty::Float(ast::FloatTy::F64) => { + ty::Float(ty::FloatTy::F64) => { let l = rustc_apfloat::ieee::Double::from_bits(a); let r = rustc_apfloat::ieee::Double::from_bits(b); l.partial_cmp(&r) } ty::Int(ity) => { - use rustc_attr::SignedInt; use rustc_middle::ty::layout::IntegerExt; - let size = rustc_target::abi::Integer::from_attr(&tcx, SignedInt(ity)).size(); + let size = rustc_target::abi::Integer::from_int_ty(&tcx, ity).size(); let a = size.sign_extend(a); let b = size.sign_extend(b); Some((a as i128).cmp(&(b as i128))) diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 83fee380cc..d7c08a2d1a 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -952,7 +952,7 @@ 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(|r| r.ty).unwrap_or(v.head().ty); + let ty = matrix.heads().next().map_or(v.head().ty, |r| r.ty); let pcx = PatCtxt { cx, ty, span: v.head().span, is_top_level }; debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head()); diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index b2604ea690..4a638ec3f8 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -14,7 +14,7 @@ mod tokentrees; mod unescape_error_reporting; mod unicode_chars; -use unescape_error_reporting::{emit_unescape_error, push_escaped_char}; +use unescape_error_reporting::{emit_unescape_error, escaped_char}; #[derive(Clone, Debug)] pub struct UnmatchedBrace { @@ -122,11 +122,9 @@ impl<'a> StringReader<'a> { m: &str, c: char, ) -> DiagnosticBuilder<'a> { - let mut m = m.to_string(); - m.push_str(": "); - push_escaped_char(&mut m, c); - - self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), &m[..]) + self.sess + .span_diagnostic + .struct_span_fatal(self.mk_sp(from_pos, to_pos), &format!("{}: {}", m, escaped_char(c))) } /// Turns simple `rustc_lexer::TokenKind` enum into a rich @@ -421,7 +419,7 @@ impl<'a> StringReader<'a> { let content_start = start + BytePos(prefix_len); let content_end = suffix_start - BytePos(postfix_len); let id = self.symbol_from_to(content_start, content_end); - self.validate_literal_escape(mode, content_start, content_end); + self.validate_literal_escape(mode, content_start, content_end, prefix_len, postfix_len); (lit_kind, id) } @@ -525,13 +523,20 @@ impl<'a> StringReader<'a> { .raise(); } - fn validate_literal_escape(&self, mode: Mode, content_start: BytePos, content_end: BytePos) { + fn validate_literal_escape( + &self, + mode: Mode, + content_start: BytePos, + content_end: BytePos, + prefix_len: u32, + postfix_len: u32, + ) { let lit_content = self.str_from_to(content_start, content_end); unescape::unescape_literal(lit_content, mode, &mut |range, result| { // Here we only check for errors. The actual unescaping is done later. if let Err(err) = result { - let span_with_quotes = - self.mk_sp(content_start - BytePos(1), content_end + BytePos(1)); + let span_with_quotes = self + .mk_sp(content_start - BytePos(prefix_len), content_end + BytePos(postfix_len)); let (start, end) = (range.start as u32, range.end as u32); let lo = content_start + BytePos(start); let hi = lo + BytePos(end - start); diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 6a890be369..a580f0c55d 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -37,20 +37,22 @@ pub(crate) fn emit_unescape_error( EscapeError::LoneSurrogateUnicodeEscape => { handler .struct_span_err(span, "invalid unicode character escape") + .span_label(span, "invalid escape") .help("unicode escape must not be a surrogate") .emit(); } EscapeError::OutOfRangeUnicodeEscape => { handler .struct_span_err(span, "invalid unicode character escape") + .span_label(span, "invalid escape") .help("unicode escape must be at most 10FFFF") .emit(); } EscapeError::MoreThanOneChar => { - let msg = if mode.is_bytes() { - "if you meant to write a byte string literal, use double quotes" + let (prefix, msg) = if mode.is_bytes() { + ("b", "if you meant to write a byte string literal, use double quotes") } else { - "if you meant to write a `str` literal, use double quotes" + ("", "if you meant to write a `str` literal, use double quotes") }; handler @@ -61,31 +63,44 @@ pub(crate) fn emit_unescape_error( .span_suggestion( span_with_quotes, msg, - format!("\"{}\"", lit), + format!("{}\"{}\"", prefix, lit), Applicability::MachineApplicable, ) .emit(); } EscapeError::EscapeOnlyChar => { - let (c, _span) = last_char(); + let (c, char_span) = last_char(); - let mut msg = if mode.is_bytes() { - "byte constant must be escaped: " + let msg = if mode.is_bytes() { + "byte constant must be escaped" } else { - "character constant must be escaped: " - } - .to_string(); - push_escaped_char(&mut msg, c); - - handler.span_err(span, msg.as_str()) + "character constant must be escaped" + }; + handler + .struct_span_err(span, &format!("{}: `{}`", msg, escaped_char(c))) + .span_suggestion( + char_span, + "escape the character", + c.escape_default().to_string(), + Applicability::MachineApplicable, + ) + .emit() } EscapeError::BareCarriageReturn => { let msg = if mode.in_double_quotes() { - "bare CR not allowed in string, use \\r instead" + "bare CR not allowed in string, use `\\r` instead" } else { - "character constant must be escaped: \\r" + "character constant must be escaped: `\\r`" }; - handler.span_err(span, msg); + handler + .struct_span_err(span, msg) + .span_suggestion( + span, + "escape the character", + "\\r".to_string(), + Applicability::MachineApplicable, + ) + .emit(); } EscapeError::BareCarriageReturnInRawString => { assert!(mode.in_double_quotes()); @@ -97,21 +112,22 @@ pub(crate) fn emit_unescape_error( let label = if mode.is_bytes() { "unknown byte escape" } else { "unknown character escape" }; - let mut msg = label.to_string(); - msg.push_str(": "); - push_escaped_char(&mut msg, c); - - let mut diag = handler.struct_span_err(span, msg.as_str()); + let ec = escaped_char(c); + let mut diag = handler.struct_span_err(span, &format!("{}: `{}`", label, ec)); diag.span_label(span, label); if c == '{' || c == '}' && !mode.is_bytes() { diag.help( - "if used in a formatting string, \ - curly braces are escaped with `{{` and `}}`", + "if used in a formatting string, curly braces are escaped with `{{` and `}}`", ); } else if c == '\r' { diag.help( - "this is an isolated carriage return; \ - consider checking your editor and version control settings", + "this is an isolated carriage return; consider checking your editor and \ + version control settings", + ); + } else { + diag.help( + "for more information, visit \ + ", ); } diag.emit(); @@ -122,45 +138,70 @@ pub(crate) fn emit_unescape_error( EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => { let (c, span) = last_char(); - let mut msg = if error == EscapeError::InvalidCharInHexEscape { - "invalid character in numeric character escape: " + let msg = if error == EscapeError::InvalidCharInHexEscape { + "invalid character in numeric character escape" } else { - "invalid character in unicode escape: " - } - .to_string(); - push_escaped_char(&mut msg, c); + "invalid character in unicode escape" + }; + let c = escaped_char(c); - handler.span_err(span, msg.as_str()) + handler + .struct_span_err(span, &format!("{}: `{}`", msg, c)) + .span_label(span, msg) + .emit(); } EscapeError::NonAsciiCharInByte => { assert!(mode.is_bytes()); - let (_c, span) = last_char(); - handler.span_err( - span, - "byte constant must be ASCII. \ - Use a \\xHH escape for a non-ASCII byte", - ) + let (c, span) = last_char(); + handler + .struct_span_err(span, "non-ASCII character in byte constant") + .span_label(span, "byte constant must be ASCII") + .span_suggestion( + span, + "use a \\xHH escape for a non-ASCII byte", + format!("\\x{:X}", c as u32), + Applicability::MachineApplicable, + ) + .emit(); } EscapeError::NonAsciiCharInByteString => { assert!(mode.is_bytes()); let (_c, span) = last_char(); - handler.span_err(span, "raw byte string must be ASCII") + handler + .struct_span_err(span, "raw byte string must be ASCII") + .span_label(span, "must be ASCII") + .emit(); + } + EscapeError::OutOfRangeHexEscape => { + handler + .struct_span_err(span, "out of range hex escape") + .span_label(span, "must be a character in the range [\\x00-\\x7f]") + .emit(); } - EscapeError::OutOfRangeHexEscape => handler.span_err( - span, - "this form of character escape may only be used \ - with characters in the range [\\x00-\\x7f]", - ), EscapeError::LeadingUnderscoreUnicodeEscape => { - let (_c, span) = last_char(); - handler.span_err(span, "invalid start of unicode escape") + let (c, span) = last_char(); + let msg = "invalid start of unicode escape"; + handler + .struct_span_err(span, &format!("{}: `{}`", msg, c)) + .span_label(span, msg) + .emit(); } EscapeError::OverlongUnicodeEscape => { - handler.span_err(span, "overlong unicode escape (must have at most 6 hex digits)") - } - EscapeError::UnclosedUnicodeEscape => { - handler.span_err(span, "unterminated unicode escape (needed a `}`)") + handler + .struct_span_err(span, "overlong unicode escape") + .span_label(span, "must have at most 6 hex digits") + .emit(); } + EscapeError::UnclosedUnicodeEscape => handler + .struct_span_err(span, "unterminated unicode escape") + .span_label(span, "missing a closing `}`") + .span_suggestion_verbose( + span.shrink_to_hi(), + "terminate the unicode escape", + "}".to_string(), + Applicability::MaybeIncorrect, + ) + .emit(), EscapeError::NoBraceInUnicodeEscape => { let msg = "incorrect unicode escape sequence"; let mut diag = handler.struct_span_err(span, msg); @@ -190,28 +231,38 @@ pub(crate) fn emit_unescape_error( diag.emit(); } - EscapeError::UnicodeEscapeInByte => handler.span_err( - span, - "unicode escape sequences cannot be used \ - as a byte or in a byte string", - ), + EscapeError::UnicodeEscapeInByte => { + let msg = "unicode escape in byte string"; + handler + .struct_span_err(span, msg) + .span_label(span, msg) + .help("unicode escape sequences cannot be used as a byte or in a byte string") + .emit(); + } EscapeError::EmptyUnicodeEscape => { - handler.span_err(span, "empty unicode escape (must have at least 1 hex digit)") + handler + .struct_span_err(span, "empty unicode escape") + .span_label(span, "this escape must have at least 1 hex digit") + .emit(); + } + EscapeError::ZeroChars => { + let msg = "empty character literal"; + handler.struct_span_err(span, msg).span_label(span, msg).emit() + } + EscapeError::LoneSlash => { + let msg = "invalid trailing slash in literal"; + handler.struct_span_err(span, msg).span_label(span, msg).emit(); } - EscapeError::ZeroChars => handler.span_err(span, "empty character literal"), - EscapeError::LoneSlash => handler.span_err(span, "invalid trailing slash in literal"), } } /// Pushes a character to a message string for error reporting -pub(crate) fn push_escaped_char(msg: &mut String, c: char) { +pub(crate) fn escaped_char(c: char) -> String { match c { '\u{20}'..='\u{7e}' => { // Don't escape \, ' or " for user-facing messages - msg.push(c); - } - _ => { - msg.extend(c.escape_default()); + c.to_string() } + _ => c.escape_default().to_string(), } } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 44999c9b63..f155f3a94e 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -4,25 +4,23 @@ #![feature(bindings_after_at)] #![feature(iter_order_by)] #![feature(or_patterns)] +#![feature(box_syntax)] +#![feature(box_patterns)] use rustc_ast as ast; use rustc_ast::attr::HasAttrs; -use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{self, LazyTokenStream, TokenStream, TokenTree}; +use rustc_ast::token::{self, Nonterminal}; +use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens, LazyTokenStream, TokenStream}; use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; use rustc_session::parse::ParseSess; -use rustc_span::{symbol::kw, FileName, SourceFile, Span, DUMMY_SP}; +use rustc_span::{FileName, SourceFile, Span}; -use smallvec::SmallVec; -use std::cell::RefCell; -use std::mem; use std::path::Path; use std::str; -use tracing::{debug, info}; +use tracing::debug; pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); @@ -237,7 +235,11 @@ pub fn parse_in<'a, T>( // NOTE(Centril): The following probably shouldn't be here but it acknowledges the // fact that architecturally, we are using parsing (read on below to understand why). -pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> TokenStream { +pub fn nt_to_tokenstream( + nt: &Nonterminal, + sess: &ParseSess, + synthesize_tokens: CanSynthesizeMissingTokens, +) -> TokenStream { // A `Nonterminal` is often a parsed AST item. At this point we now // need to convert the parsed AST to an actual token stream, e.g. // un-parse it basically. @@ -255,9 +257,18 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke |tokens: Option<&LazyTokenStream>| tokens.as_ref().map(|t| t.create_token_stream()); let tokens = match *nt { - Nonterminal::NtItem(ref item) => prepend_attrs(&item.attrs, item.tokens.as_ref()), + Nonterminal::NtItem(ref item) => prepend_attrs(sess, &item.attrs, nt, item.tokens.as_ref()), Nonterminal::NtBlock(ref block) => convert_tokens(block.tokens.as_ref()), - Nonterminal::NtStmt(ref stmt) => prepend_attrs(stmt.attrs(), stmt.tokens()), + Nonterminal::NtStmt(ref stmt) => { + let do_prepend = |tokens| prepend_attrs(sess, stmt.attrs(), nt, tokens); + if let ast::StmtKind::Empty = stmt.kind { + let tokens: TokenStream = + tokenstream::TokenTree::token(token::Semi, stmt.span).into(); + do_prepend(Some(&LazyTokenStream::new(tokens))) + } else { + do_prepend(stmt.tokens()) + } + } Nonterminal::NtPat(ref pat) => convert_tokens(pat.tokens.as_ref()), Nonterminal::NtTy(ref ty) => convert_tokens(ty.tokens.as_ref()), Nonterminal::NtIdent(ident, is_raw) => { @@ -274,376 +285,43 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke if expr.tokens.is_none() { debug!("missing tokens for expr {:?}", expr); } - prepend_attrs(&expr.attrs, expr.tokens.as_ref()) + prepend_attrs(sess, &expr.attrs, nt, expr.tokens.as_ref()) } }; - // Caches the stringification of 'good' `TokenStreams` which passed - // `tokenstream_probably_equal_for_proc_macro`. This allows us to avoid - // repeatedly stringifying and comparing the same `TokenStream` for deeply - // nested nonterminals. - // - // We cache by the strinification instead of the `TokenStream` to avoid - // needing to implement `Hash` for `TokenStream`. Note that it's possible to - // have two distinct `TokenStream`s that stringify to the same result - // (e.g. if they differ only in hygiene information). However, any - // information lost during the stringification process is also intentionally - // ignored by `tokenstream_probably_equal_for_proc_macro`, so it's fine - // that a single cache entry may 'map' to multiple distinct `TokenStream`s. - // - // This is a temporary hack to prevent compilation blowup on certain inputs. - // The entire pretty-print/retokenize process will be removed soon. - thread_local! { - static GOOD_TOKEN_CACHE: RefCell> = Default::default(); - } - - // FIXME(#43081): Avoid this pretty-print + reparse hack - // Pretty-print the AST struct without inserting any parenthesis - // beyond those explicitly written by the user (e.g. `ExpnKind::Paren`). - // The resulting stream may have incorrect precedence, but it's only - // ever used for a comparison against the capture tokenstream. - let source = pprust::nonterminal_to_string_no_extra_parens(nt); - let filename = FileName::macro_expansion_source_code(&source); - let reparsed_tokens = parse_stream_from_source_str(filename, source.clone(), sess, Some(span)); - - // During early phases of the compiler the AST could get modified - // directly (e.g., attributes added or removed) and the internal cache - // of tokens my not be invalidated or updated. Consequently if the - // "lossless" token stream disagrees with our actual stringification - // (which has historically been much more battle-tested) then we go - // with the lossy stream anyway (losing span information). - // - // Note that the comparison isn't `==` here to avoid comparing spans, - // but it *also* is a "probable" equality which is a pretty weird - // definition. We mostly want to catch actual changes to the AST - // like a `#[cfg]` being processed or some weird `macro_rules!` - // expansion. - // - // What we *don't* want to catch is the fact that a user-defined - // literal like `0xf` is stringified as `15`, causing the cached token - // stream to not be literal `==` token-wise (ignoring spans) to the - // token stream we got from stringification. - // - // Instead the "probably equal" check here is "does each token - // recursively have the same discriminant?" We basically don't look at - // the token values here and assume that such fine grained token stream - // modifications, including adding/removing typically non-semantic - // tokens such as extra braces and commas, don't happen. if let Some(tokens) = tokens { - if GOOD_TOKEN_CACHE.with(|cache| cache.borrow().contains(&source)) { - return tokens; - } - - // Compare with a non-relaxed delim match to start. - if tokenstream_probably_equal_for_proc_macro(&tokens, &reparsed_tokens, sess, false) { - GOOD_TOKEN_CACHE.with(|cache| cache.borrow_mut().insert(source.clone())); - return tokens; - } - - // The check failed. This time, we pretty-print the AST struct with parenthesis - // inserted to preserve precedence. This may cause `None`-delimiters in the captured - // token stream to match up with inserted parenthesis in the reparsed stream. - let source_with_parens = pprust::nonterminal_to_string(nt); - let filename_with_parens = FileName::macro_expansion_source_code(&source_with_parens); - - if GOOD_TOKEN_CACHE.with(|cache| cache.borrow().contains(&source_with_parens)) { - return tokens; - } - - let reparsed_tokens_with_parens = parse_stream_from_source_str( - filename_with_parens, - source_with_parens, - sess, - Some(span), - ); - - // Compare with a relaxed delim match - we want inserted parenthesis in the - // reparsed stream to match `None`-delimiters in the original stream. - if tokenstream_probably_equal_for_proc_macro( - &tokens, - &reparsed_tokens_with_parens, - sess, - true, - ) { - GOOD_TOKEN_CACHE.with(|cache| cache.borrow_mut().insert(source.clone())); - return tokens; - } - - info!( - "cached tokens found, but they're not \"probably equal\", \ - going with stringified version" - ); - info!("cached tokens: {}", pprust::tts_to_string(&tokens)); - info!("reparsed tokens: {}", pprust::tts_to_string(&reparsed_tokens_with_parens)); - - info!("cached tokens debug: {:?}", tokens); - info!("reparsed tokens debug: {:?}", reparsed_tokens_with_parens); + return tokens; + } else if matches!(synthesize_tokens, CanSynthesizeMissingTokens::Yes) { + return fake_token_stream(sess, nt); + } else { + panic!("Missing tokens for nt at {:?}: {:?}", nt.span(), pprust::nonterminal_to_string(nt)); } - reparsed_tokens } -// See comments in `Nonterminal::to_tokenstream` for why we care about -// *probably* equal here rather than actual equality -// -// This is otherwise the same as `eq_unspanned`, only recursing with a -// different method. -pub fn tokenstream_probably_equal_for_proc_macro( - tokens: &TokenStream, - reparsed_tokens: &TokenStream, - sess: &ParseSess, - relaxed_delim_match: bool, -) -> bool { - // When checking for `probably_eq`, we ignore certain tokens that aren't - // preserved in the AST. Because they are not preserved, the pretty - // printer arbitrarily adds or removes them when printing as token - // streams, making a comparison between a token stream generated from an - // AST and a token stream which was parsed into an AST more reliable. - fn semantic_tree(tree: &TokenTree) -> bool { - if let TokenTree::Token(token) = tree { - if let - // The pretty printer tends to add trailing commas to - // everything, and in particular, after struct fields. - | token::Comma - // The pretty printer collapses many semicolons into one. - | token::Semi - // We don't preserve leading `|` tokens in patterns, so - // we ignore them entirely - | token::BinOp(token::BinOpToken::Or) - // We don't preserve trailing '+' tokens in trait bounds, - // so we ignore them entirely - | token::BinOp(token::BinOpToken::Plus) - // The pretty printer can turn `$crate` into `::crate_name` - | token::ModSep = token.kind { - return false; - } - } - true - } - - // When comparing two `TokenStream`s, we ignore the `IsJoint` information. - // - // However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will - // use `Token.glue` on adjacent tokens with the proper `IsJoint`. - // Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`) - // and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent - // when determining if two `TokenStream`s are 'probably equal'. - // - // Therefore, we use `break_two_token_op` to convert all tokens - // to the 'unglued' form (if it exists). This ensures that two - // `TokenStream`s which differ only in how their tokens are glued - // will be considered 'probably equal', which allows us to keep spans. - // - // This is important when the original `TokenStream` contained - // extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces - // will be omitted when we pretty-print, which can cause the original - // and reparsed `TokenStream`s to differ in the assignment of `IsJoint`, - // leading to some tokens being 'glued' together in one stream but not - // the other. See #68489 for more details. - fn break_tokens(tree: TokenTree) -> impl Iterator { - // In almost all cases, we should have either zero or one levels - // of 'unglueing'. However, in some unusual cases, we may need - // to iterate breaking tokens mutliple times. For example: - // '[BinOpEq(Shr)] => [Gt, Ge] -> [Gt, Gt, Eq]' - let mut token_trees: SmallVec<[_; 2]>; - if let TokenTree::Token(token) = tree { - let mut out = SmallVec::<[_; 2]>::new(); - out.push(token); - // Iterate to fixpoint: - // * We start off with 'out' containing our initial token, and `temp` empty - // * If we are able to break any tokens in `out`, then `out` will have - // at least one more element than 'temp', so we will try to break tokens - // again. - // * If we cannot break any tokens in 'out', we are done - loop { - let mut temp = SmallVec::<[_; 2]>::new(); - let mut changed = false; - - for token in out.into_iter() { - if let Some((first, second)) = token.kind.break_two_token_op() { - temp.push(Token::new(first, DUMMY_SP)); - temp.push(Token::new(second, DUMMY_SP)); - changed = true; - } else { - temp.push(token); - } - } - out = temp; - if !changed { - break; - } - } - token_trees = out.into_iter().map(TokenTree::Token).collect(); - } else { - token_trees = SmallVec::new(); - token_trees.push(tree); - } - token_trees.into_iter() - } - - fn expand_token(tree: TokenTree, sess: &ParseSess) -> impl Iterator { - // When checking tokenstreams for 'probable equality', we are comparing - // a captured (from parsing) `TokenStream` to a reparsed tokenstream. - // The reparsed Tokenstream will never have `None`-delimited groups, - // since they are only ever inserted as a result of macro expansion. - // Therefore, inserting a `None`-delimtied group here (when we - // convert a nested `Nonterminal` to a tokenstream) would cause - // a mismatch with the reparsed tokenstream. - // - // Note that we currently do not handle the case where the - // reparsed stream has a `Parenthesis`-delimited group - // inserted. This will cause a spurious mismatch: - // issue #75734 tracks resolving this. - - let expanded: SmallVec<[_; 1]> = - if let TokenTree::Token(Token { kind: TokenKind::Interpolated(nt), span }) = &tree { - nt_to_tokenstream(nt, sess, *span) - .into_trees() - .flat_map(|t| expand_token(t, sess)) - .collect() - } else { - // Filter before and after breaking tokens, - // since we may want to ignore both glued and unglued tokens. - std::iter::once(tree) - .filter(semantic_tree) - .flat_map(break_tokens) - .filter(semantic_tree) - .collect() - }; - expanded.into_iter() - } - - // Break tokens after we expand any nonterminals, so that we break tokens - // that are produced as a result of nonterminal expansion. - let tokens = tokens.trees().flat_map(|t| expand_token(t, sess)); - let reparsed_tokens = reparsed_tokens.trees().flat_map(|t| expand_token(t, sess)); - - tokens.eq_by(reparsed_tokens, |t, rt| { - tokentree_probably_equal_for_proc_macro(&t, &rt, sess, relaxed_delim_match) - }) -} - -// See comments in `Nonterminal::to_tokenstream` for why we care about -// *probably* equal here rather than actual equality -// -// This is otherwise the same as `eq_unspanned`, only recursing with a -// different method. -pub fn tokentree_probably_equal_for_proc_macro( - token: &TokenTree, - reparsed_token: &TokenTree, - sess: &ParseSess, - relaxed_delim_match: bool, -) -> bool { - match (token, reparsed_token) { - (TokenTree::Token(token), TokenTree::Token(reparsed_token)) => { - token_probably_equal_for_proc_macro(token, reparsed_token) - } - ( - TokenTree::Delimited(_, delim, tokens), - TokenTree::Delimited(_, reparsed_delim, reparsed_tokens), - ) if delim == reparsed_delim => tokenstream_probably_equal_for_proc_macro( - tokens, - reparsed_tokens, - sess, - relaxed_delim_match, - ), - (TokenTree::Delimited(_, DelimToken::NoDelim, tokens), reparsed_token) => { - if relaxed_delim_match { - if let TokenTree::Delimited(_, DelimToken::Paren, reparsed_tokens) = reparsed_token - { - if tokenstream_probably_equal_for_proc_macro( - tokens, - reparsed_tokens, - sess, - relaxed_delim_match, - ) { - return true; - } - } - } - tokens.len() == 1 - && tokentree_probably_equal_for_proc_macro( - &tokens.trees().next().unwrap(), - reparsed_token, - sess, - relaxed_delim_match, - ) - } - _ => false, - } -} - -// See comments in `Nonterminal::to_tokenstream` for why we care about -// *probably* equal here rather than actual equality -fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool { - if mem::discriminant(&first.kind) != mem::discriminant(&other.kind) { - return false; - } - use rustc_ast::token::TokenKind::*; - match (&first.kind, &other.kind) { - (&Eq, &Eq) - | (&Lt, &Lt) - | (&Le, &Le) - | (&EqEq, &EqEq) - | (&Ne, &Ne) - | (&Ge, &Ge) - | (&Gt, &Gt) - | (&AndAnd, &AndAnd) - | (&OrOr, &OrOr) - | (&Not, &Not) - | (&Tilde, &Tilde) - | (&At, &At) - | (&Dot, &Dot) - | (&DotDot, &DotDot) - | (&DotDotDot, &DotDotDot) - | (&DotDotEq, &DotDotEq) - | (&Comma, &Comma) - | (&Semi, &Semi) - | (&Colon, &Colon) - | (&ModSep, &ModSep) - | (&RArrow, &RArrow) - | (&LArrow, &LArrow) - | (&FatArrow, &FatArrow) - | (&Pound, &Pound) - | (&Dollar, &Dollar) - | (&Question, &Question) - | (&Eof, &Eof) => true, - - (&BinOp(a), &BinOp(b)) | (&BinOpEq(a), &BinOpEq(b)) => a == b, - - (&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b, - - (&DocComment(a1, a2, a3), &DocComment(b1, b2, b3)) => a1 == b1 && a2 == b2 && a3 == b3, - - (&Literal(a), &Literal(b)) => a == b, - - (&Lifetime(a), &Lifetime(b)) => a == b, - (&Ident(a, b), &Ident(c, d)) => { - b == d && (a == c || a == kw::DollarCrate || c == kw::DollarCrate) - } - - (&Interpolated(..), &Interpolated(..)) => panic!("Unexpanded Interpolated!"), - - _ => panic!("forgot to add a token?"), - } +pub fn fake_token_stream(sess: &ParseSess, nt: &Nonterminal) -> TokenStream { + let source = pprust::nonterminal_to_string(nt); + let filename = FileName::macro_expansion_source_code(&source); + parse_stream_from_source_str(filename, source, sess, Some(nt.span())) } fn prepend_attrs( + sess: &ParseSess, attrs: &[ast::Attribute], + nt: &Nonterminal, tokens: Option<&tokenstream::LazyTokenStream>, ) -> Option { - let tokens = tokens?.create_token_stream(); if attrs.is_empty() { - return Some(tokens); + return Some(tokens?.create_token_stream()); } let mut builder = tokenstream::TokenStreamBuilder::new(); for attr in attrs { // FIXME: Correctly handle tokens for inner attributes. // For now, we fall back to reparsing the original AST node if attr.style == ast::AttrStyle::Inner { - return None; + return Some(fake_token_stream(sess, nt)); } builder.push(attr.tokens()); } - builder.push(tokens); + builder.push(tokens?.create_token_stream()); Some(builder.build()) } diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index fae09fa6fe..1b26fb3337 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -89,7 +89,7 @@ impl<'a> Parser<'a> { inner_parse_policy, self.token ); let lo = self.token.span; - let ((item, style, span), tokens) = self.collect_tokens(|this| { + self.collect_tokens(|this| { if this.eat(&token::Pound) { let style = if this.eat(&token::Not) { ast::AttrStyle::Inner @@ -107,15 +107,13 @@ impl<'a> Parser<'a> { this.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy); } - Ok((item, style, attr_sp)) + Ok(attr::mk_attr_from_item(item, None, style, attr_sp)) } else { let token_str = pprust::token_to_string(&this.token); let msg = &format!("expected `#`, found `{}`", token_str); Err(this.struct_span_err(this.token.span, msg)) } - })?; - - Ok(attr::mk_attr_from_item(item, tokens, style, span)) + }) } pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) { @@ -165,13 +163,7 @@ impl<'a> Parser<'a> { let args = this.parse_attr_args()?; Ok(ast::AttrItem { path, args, tokens: None }) }; - if capture_tokens { - let (mut item, tokens) = self.collect_tokens(do_parse)?; - item.tokens = tokens; - item - } else { - do_parse(self)? - } + if capture_tokens { self.collect_tokens(do_parse) } else { do_parse(self) }? }) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 98c7b9a63a..5512e849c4 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2,14 +2,13 @@ use super::ty::AllowPlus; use super::TokenType; use super::{BlockMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType}; +use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; -use rustc_ast::{ - self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, - Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item, ItemKind, Mutability, Param, Pat, - PatKind, Path, PathSegment, QSelf, Ty, TyKind, -}; +use rustc_ast::{AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec}; +use rustc_ast::{BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item}; +use rustc_ast::{ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty, TyKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err}; @@ -220,6 +219,7 @@ impl<'a> Parser<'a> { edible: &[TokenKind], inedible: &[TokenKind], ) -> PResult<'a, bool /* recovered */> { + debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible); fn tokens_to_string(tokens: &[TokenType]) -> String { let mut i = tokens.iter(); // This might be a sign we need a connect method on `Iterator`. @@ -245,6 +245,7 @@ impl<'a> Parser<'a> { .collect::>(); expected.sort_by_cached_key(|x| x.to_string()); expected.dedup(); + let expect = tokens_to_string(&expected[..]); let actual = super::token_descr(&self.token); let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { @@ -270,6 +271,16 @@ impl<'a> Parser<'a> { }; self.last_unexpected_token_span = Some(self.token.span); let mut err = self.struct_span_err(self.token.span, &msg_exp); + + // Add suggestion for a missing closing angle bracket if '>' is included in expected_tokens + // there are unclosed angle brackets + if self.unmatched_angle_bracket_count > 0 + && self.token.kind == TokenKind::Eq + && expected.iter().any(|tok| matches!(tok, TokenType::Token(TokenKind::Gt))) + { + err.span_label(self.prev_token.span, "maybe try to close unmatched angle bracket"); + } + let sp = if self.token == token::Eof { // This is EOF; don't want to point at the following char, but rather the last token. self.prev_token.span @@ -511,7 +522,7 @@ impl<'a> Parser<'a> { // // `x.foo::>>(3)` let parsed_angle_bracket_args = - segment.args.as_ref().map(|args| args.is_angle_bracketed()).unwrap_or(false); + segment.args.as_ref().map_or(false, |args| args.is_angle_bracketed()); debug!( "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}", @@ -1093,7 +1104,7 @@ impl<'a> Parser<'a> { let (prev_sp, sp) = match (&self.token.kind, self.subparser_name) { // Point at the end of the macro call when reaching end of macro arguments. (token::Eof, Some(_)) => { - let sp = self.sess.source_map().next_point(self.token.span); + let sp = self.sess.source_map().next_point(self.prev_token.span); (sp, sp) } // We don't want to point at the following span after DUMMY_SP. @@ -1710,7 +1721,7 @@ impl<'a> Parser<'a> { pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a> { let (span, msg) = match (&self.token.kind, self.subparser_name) { (&token::Eof, Some(origin)) => { - let sp = self.sess.source_map().next_point(self.token.span); + let sp = self.sess.source_map().next_point(self.prev_token.span); (sp, format!("expected expression, found end of {}", origin)) } _ => ( diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b147f42fad..cfd7ad4822 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -15,6 +15,7 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; +use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; @@ -471,7 +472,12 @@ impl<'a> Parser<'a> { /// Parses a prefix-unary-operator expr. fn parse_prefix_expr(&mut self, attrs: Option) -> PResult<'a, P> { let attrs = self.parse_or_use_outer_attributes(attrs)?; - self.maybe_collect_tokens(super::attr::maybe_needs_tokens(&attrs), |this| { + // FIXME: Use super::attr::maybe_needs_tokens(&attrs) once we come up + // with a good way of passing `force_tokens` through from `parse_nonterminal`. + // Checking !attrs.is_empty() is correct, but will cause us to unnecessarily + // capture tokens in some circumstances. + let needs_tokens = !attrs.is_empty(); + let do_parse = |this: &mut Parser<'a>| { let lo = this.token.span; // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() let (hi, ex) = match this.token.uninterpolate().kind { @@ -487,7 +493,8 @@ impl<'a> Parser<'a> { _ => return this.parse_dot_or_call_expr(Some(attrs)), }?; Ok(this.mk_expr(lo.to(hi), ex, attrs)) - }) + }; + if needs_tokens { self.collect_tokens(do_parse) } else { do_parse(self) } } fn parse_prefix_expr_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { @@ -578,7 +585,7 @@ impl<'a> Parser<'a> { lhs_span: Span, expr_kind: fn(P, P) -> ExprKind, ) -> PResult<'a, P> { - let mk_expr = |this: &mut Self, rhs: P| { + let mk_expr = |this: &mut Self, lhs: P, rhs: P| { this.mk_expr( this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs), @@ -590,13 +597,49 @@ impl<'a> Parser<'a> { // LessThan comparison after this cast. let parser_snapshot_before_type = self.clone(); let cast_expr = match self.parse_ty_no_plus() { - Ok(rhs) => mk_expr(self, rhs), + Ok(rhs) => mk_expr(self, lhs, rhs), Err(mut type_err) => { // Rewind to before attempting to parse the type with generics, to recover // from situations like `x as usize < y` in which we first tried to parse // `usize < y` as a type with generic arguments. let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type); + // Check for typo of `'a: loop { break 'a }` with a missing `'`. + match (&lhs.kind, &self.token.kind) { + ( + // `foo: ` + ExprKind::Path(None, ast::Path { segments, .. }), + TokenKind::Ident(kw::For | kw::Loop | kw::While, false), + ) if segments.len() == 1 => { + let snapshot = self.clone(); + let label = Label { + ident: Ident::from_str_and_span( + &format!("'{}", segments[0].ident), + segments[0].ident.span, + ), + }; + match self.parse_labeled_expr(label, AttrVec::new(), false) { + Ok(expr) => { + type_err.cancel(); + self.struct_span_err(label.ident.span, "malformed loop label") + .span_suggestion( + label.ident.span, + "use the correct loop label format", + label.ident.to_string(), + Applicability::MachineApplicable, + ) + .emit(); + return Ok(expr); + } + Err(mut err) => { + err.cancel(); + *self = snapshot; + } + } + } + _ => {} + } + match self.parse_path(PathStyle::Expr) { Ok(path) => { let (op_noun, op_verb) = match self.token.kind { @@ -623,7 +666,8 @@ impl<'a> Parser<'a> { op_noun, ); let span_after_type = parser_snapshot_after_type.token.span; - let expr = mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path))); + let expr = + mk_expr(self, lhs, self.mk_ty(path.span, TyKind::Path(None, path))); let expr_str = self .span_to_snippet(expr.span) @@ -1060,7 +1104,7 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::While) { self.parse_while_expr(None, self.prev_token.span, attrs) } else if let Some(label) = self.eat_label() { - self.parse_labeled_expr(label, attrs) + self.parse_labeled_expr(label, attrs, true) } else if self.eat_keyword(kw::Loop) { self.parse_loop_expr(None, self.prev_token.span, attrs) } else if self.eat_keyword(kw::Continue) { @@ -1124,20 +1168,6 @@ impl<'a> Parser<'a> { } } - fn maybe_collect_tokens( - &mut self, - needs_tokens: bool, - f: impl FnOnce(&mut Self) -> PResult<'a, P>, - ) -> PResult<'a, P> { - if needs_tokens { - let (mut expr, tokens) = self.collect_tokens(f)?; - expr.tokens = tokens; - Ok(expr) - } else { - f(self) - } - } - fn parse_lit_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.token.span; match self.parse_opt_lit() { @@ -1235,7 +1265,12 @@ impl<'a> Parser<'a> { } /// Parse `'label: $expr`. The label is already parsed. - fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P> { + fn parse_labeled_expr( + &mut self, + label: Label, + attrs: AttrVec, + consume_colon: bool, + ) -> PResult<'a, P> { let lo = label.ident.span; let label = Some(label); let ate_colon = self.eat(&token::Colon); @@ -1254,7 +1289,7 @@ impl<'a> Parser<'a> { self.parse_expr() }?; - if !ate_colon { + if !ate_colon && consume_colon { self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span); } @@ -1598,10 +1633,6 @@ impl<'a> Parser<'a> { } else { Async::No }; - if let Async::Yes { span, .. } = asyncness { - // Feature-gate `async ||` closures. - self.sess.gated_spans.gate(sym::async_closure, span); - } let capture_clause = self.parse_capture_clause()?; let decl = self.parse_fn_block_decl()?; @@ -1618,6 +1649,11 @@ impl<'a> Parser<'a> { } }; + if let Async::Yes { span, .. } = asyncness { + // Feature-gate `async ||` closures. + self.sess.gated_spans.gate(sym::async_closure, span); + } + Ok(self.mk_expr( lo.to(body.span), ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)), @@ -2108,8 +2144,8 @@ impl<'a> Parser<'a> { let mut async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| { recover_async = true; - e.span_label(span, "`async` blocks are only allowed in the 2018 edition"); - e.help("set `edition = \"2018\"` in `Cargo.toml`"); + e.span_label(span, "`async` blocks are only allowed in Rust 2018 or later"); + e.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)); e.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); }; diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index ed8d4f7842..42a1337686 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -56,14 +56,26 @@ impl<'a> Parser<'a> { self.expect(&token::Colon)?; let ty = self.parse_ty()?; - self.sess.gated_spans.gate(sym::min_const_generics, const_span.to(self.prev_token.span)); + // Parse optional const generics default value, taking care of feature gating the spans + // with the unstable syntax mechanism. + let default = if self.eat(&token::Eq) { + // The gated span goes from the `=` to the end of the const argument that follows (and + // which could be a block expression). + let start = self.prev_token.span; + let const_arg = self.parse_const_arg()?; + let span = start.to(const_arg.value.span); + self.sess.gated_spans.gate(sym::const_generics_defaults, span); + Some(const_arg) + } else { + None + }; Ok(GenericParam { ident, id: ast::DUMMY_NODE_ID, attrs: preceding_attrs.into(), bounds: Vec::new(), - kind: GenericParamKind::Const { ty, kw_span: const_span }, + kind: GenericParamKind::Const { ty, kw_span: const_span, default }, is_placeholder: false, }) } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 634cce403d..ee24286241 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,14 +1,14 @@ use super::diagnostics::{dummy_arg, ConsumeClosingDelim, Error}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; -use super::{FollowedByType, Parser, PathStyle}; +use super::{FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; -use crate::maybe_whole; +use crate::{maybe_collect_tokens, maybe_whole}; +use rustc_ast::ast::*; use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; -use rustc_ast::{AssocItem, AssocItemKind, ForeignItemKind, Item, ItemKind, Mod}; use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{BindingMode, Block, FnDecl, FnSig, Param, SelfKind}; use rustc_ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData}; @@ -16,7 +16,7 @@ use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, Visibility use rustc_ast::{MacArgs, MacCall, MacDelimiter}; use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability, PResult, StashKey}; -use rustc_span::edition::Edition; +use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; use rustc_span::source_map::{self, Span}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -69,7 +69,7 @@ impl<'a> Parser<'a> { unsafety: Unsafe, ) -> PResult<'a, Mod> { let mut items = vec![]; - while let Some(item) = self.parse_item()? { + while let Some(item) = self.parse_item(ForceCollect::No)? { items.push(item); self.maybe_consume_incorrect_semicolon(&items); } @@ -93,13 +93,17 @@ impl<'a> Parser<'a> { pub(super) type ItemInfo = (Ident, ItemKind); impl<'a> Parser<'a> { - pub fn parse_item(&mut self) -> PResult<'a, Option>> { - self.parse_item_(|_| true).map(|i| i.map(P)) + pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option>> { + self.parse_item_(|_| true, force_collect).map(|i| i.map(P)) } - fn parse_item_(&mut self, req_name: ReqName) -> PResult<'a, Option> { + fn parse_item_( + &mut self, + req_name: ReqName, + force_collect: ForceCollect, + ) -> PResult<'a, Option> { let attrs = self.parse_outer_attributes()?; - self.parse_item_common(attrs, true, false, req_name) + self.parse_item_common(attrs, true, false, req_name, force_collect) } pub(super) fn parse_item_common( @@ -108,6 +112,7 @@ impl<'a> Parser<'a> { mac_allowed: bool, attrs_allowed: bool, req_name: ReqName, + force_collect: ForceCollect, ) -> PResult<'a, Option> { maybe_whole!(self, NtItem, |item| { let mut item = item; @@ -116,28 +121,12 @@ impl<'a> Parser<'a> { Some(item.into_inner()) }); - let needs_tokens = super::attr::maybe_needs_tokens(&attrs); - let mut unclosed_delims = vec![]; - let parse_item = |this: &mut Self| { + let item = maybe_collect_tokens!(self, force_collect, &attrs, |this: &mut Self| { let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name); unclosed_delims.append(&mut this.unclosed_delims); - item - }; - - let (mut item, tokens) = if needs_tokens { - let (item, tokens) = self.collect_tokens(parse_item)?; - (item, tokens) - } else { - (parse_item(self)?, None) - }; - if let Some(item) = &mut item { - // If we captured tokens during parsing (due to encountering an `NtItem`), - // use those instead - if item.tokens.is_none() { - item.tokens = tokens; - } - } + Ok((item?, TrailingToken::None)) + })?; self.unclosed_delims.append(&mut unclosed_delims); Ok(item) @@ -220,12 +209,27 @@ impl<'a> Parser<'a> { let info = if self.eat_keyword(kw::Use) { // USE ITEM let tree = self.parse_use_tree()?; - self.expect_semi()?; + + // If wildcard or glob-like brace syntax doesn't have `;`, + // the user may not know `*` or `{}` should be the last. + if let Err(mut e) = self.expect_semi() { + match tree.kind { + UseTreeKind::Glob => { + e.note("the wildcard token must be last on the path").emit(); + } + UseTreeKind::Nested(..) => { + e.note("glob-like brace syntax must be last on the path").emit(); + } + _ => (), + } + return Err(e); + } + (Ident::invalid(), ItemKind::Use(P(tree))) } else if self.check_fn_front_matter() { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, req_name, lo)?; - (ident, ItemKind::Fn(def(), sig, generics, body)) + (ident, ItemKind::Fn(box FnKind(def(), sig, generics, body))) } else if self.eat_keyword(kw::Extern) { if self.eat_keyword(kw::Crate) { // EXTERN CRATE @@ -494,7 +498,7 @@ impl<'a> Parser<'a> { let polarity = self.parse_polarity(); // Parse both types and traits as a type, then reinterpret if necessary. - let err_path = |span| ast::Path::from_ident(Ident::new(kw::Invalid, span)); + let err_path = |span| ast::Path::from_ident(Ident::new(kw::Empty, span)); let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) { let span = self.prev_token.span.between(self.token.span); @@ -552,7 +556,7 @@ impl<'a> Parser<'a> { }; let trait_ref = TraitRef { path, ref_id: ty_first.id }; - ItemKind::Impl { + ItemKind::Impl(box ImplKind { unsafety, polarity, defaultness, @@ -561,11 +565,11 @@ impl<'a> Parser<'a> { of_trait: Some(trait_ref), self_ty: ty_second, items: impl_items, - } + }) } None => { // impl Type - ItemKind::Impl { + ItemKind::Impl(box ImplKind { unsafety, polarity, defaultness, @@ -574,7 +578,7 @@ impl<'a> Parser<'a> { of_trait: None, self_ty: ty_first, items: impl_items, - } + }) } }; @@ -714,7 +718,7 @@ impl<'a> Parser<'a> { // It's a normal trait. tps.where_clause = self.parse_where_clause()?; let items = self.parse_item_list(attrs, |p| p.parse_trait_item())?; - Ok((ident, ItemKind::Trait(is_auto, unsafety, tps, bounds, items))) + Ok((ident, ItemKind::Trait(box TraitKind(is_auto, unsafety, tps, bounds, items)))) } } @@ -728,20 +732,22 @@ impl<'a> Parser<'a> { /// Parses associated items. fn parse_assoc_item(&mut self, req_name: ReqName) -> PResult<'a, Option>>> { - Ok(self.parse_item_(req_name)?.map(|Item { attrs, id, span, vis, ident, kind, tokens }| { - let kind = match AssocItemKind::try_from(kind) { - Ok(kind) => kind, - Err(kind) => match kind { - ItemKind::Static(a, _, b) => { - self.struct_span_err(span, "associated `static` items are not allowed") - .emit(); - AssocItemKind::Const(Defaultness::Final, a, b) - } - _ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"), - }, - }; - Some(P(Item { attrs, id, span, vis, ident, kind, tokens })) - })) + Ok(self.parse_item_(req_name, ForceCollect::No)?.map( + |Item { attrs, id, span, vis, ident, kind, tokens }| { + let kind = match AssocItemKind::try_from(kind) { + Ok(kind) => kind, + Err(kind) => match kind { + ItemKind::Static(a, _, b) => { + self.struct_span_err(span, "associated `static` items are not allowed") + .emit(); + AssocItemKind::Const(Defaultness::Final, a, b) + } + _ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"), + }, + }; + Some(P(Item { attrs, id, span, vis, ident, kind, tokens })) + }, + )) } /// Parses a `type` alias with the following grammar: @@ -761,7 +767,7 @@ impl<'a> Parser<'a> { let default = if self.eat(&token::Eq) { Some(self.parse_ty()?) } else { None }; self.expect_semi()?; - Ok((ident, ItemKind::TyAlias(def, generics, bounds, default))) + Ok((ident, ItemKind::TyAlias(box TyAliasKind(def, generics, bounds, default)))) } /// Parses a `UseTree`. @@ -918,19 +924,21 @@ impl<'a> Parser<'a> { /// Parses a foreign item (one in an `extern { ... }` block). pub fn parse_foreign_item(&mut self) -> PResult<'a, Option>>> { - Ok(self.parse_item_(|_| true)?.map(|Item { attrs, id, span, vis, ident, kind, tokens }| { - let kind = match ForeignItemKind::try_from(kind) { - Ok(kind) => kind, - Err(kind) => match kind { - ItemKind::Const(_, a, b) => { - self.error_on_foreign_const(span, ident); - ForeignItemKind::Static(a, Mutability::Not, b) - } - _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), - }, - }; - Some(P(Item { attrs, id, span, vis, ident, kind, tokens })) - })) + Ok(self.parse_item_(|_| true, ForceCollect::No)?.map( + |Item { attrs, id, span, vis, ident, kind, tokens }| { + let kind = match ForeignItemKind::try_from(kind) { + Ok(kind) => kind, + Err(kind) => match kind { + ItemKind::Const(_, a, b) => { + self.error_on_foreign_const(span, ident); + ForeignItemKind::Static(a, Mutability::Not, b) + } + _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), + }, + }; + Some(P(Item { attrs, id, span, vis, ident, kind, tokens })) + }, + )) } fn error_bad_item_kind(&self, span: Span, kind: &ItemKind, ctx: &str) -> Option { @@ -1002,10 +1010,21 @@ impl<'a> Parser<'a> { ) -> PResult<'a, ItemInfo> { let impl_span = self.token.span; let mut err = self.expected_ident_found(); - let mut impl_info = self.parse_item_impl(attrs, defaultness)?; + + // Only try to recover if this is implementing a trait for a type + let mut impl_info = match self.parse_item_impl(attrs, defaultness) { + Ok(impl_info) => impl_info, + Err(mut recovery_error) => { + // Recovery failed, raise the "expected identifier" error + recovery_error.cancel(); + return Err(err); + } + }; + match impl_info.1 { - // only try to recover if this is implementing a trait for a type - ItemKind::Impl { of_trait: Some(ref trai), ref mut constness, .. } => { + ItemKind::Impl(box ImplKind { + of_trait: Some(ref trai), ref mut constness, .. + }) => { *constness = Const::Yes(const_span); let before_trait = trai.path.span.shrink_to_lo(); @@ -1020,6 +1039,7 @@ impl<'a> Parser<'a> { ItemKind::Impl { .. } => return Err(err), _ => unreachable!(), } + Ok(impl_info) } @@ -1512,7 +1532,7 @@ impl<'a> Parser<'a> { { let kw_token = self.token.clone(); let kw_str = pprust::token_to_string(&kw_token); - let item = self.parse_item()?; + let item = self.parse_item(ForceCollect::No)?; self.struct_span_err( kw_token.span, @@ -1667,9 +1687,9 @@ impl<'a> Parser<'a> { fn ban_async_in_2015(&self, span: Span) { if span.rust_2015() { let diag = self.diagnostic(); - struct_span_err!(diag, span, E0670, "`async fn` is not permitted in the 2015 edition") - .span_label(span, "to use `async fn`, switch to Rust 2018") - .help("set `edition = \"2018\"` in `Cargo.toml`") + struct_span_err!(diag, span, E0670, "`async fn` is not permitted in Rust 2015") + .span_label(span, "to use `async fn`, switch to Rust 2018 or later") + .help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)) .note("for more on editions, read https://doc.rust-lang.org/edition-guide") .emit(); } @@ -1699,7 +1719,7 @@ impl<'a> Parser<'a> { // Skip every token until next possible arg or end. p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]); // Create a placeholder argument for proper arg count (issue #34264). - Ok(dummy_arg(Ident::new(kw::Invalid, lo.to(p.prev_token.span)))) + Ok(dummy_arg(Ident::new(kw::Empty, lo.to(p.prev_token.span)))) }); // ...now that we've parsed the first argument, `self` is no longer allowed. first_param = false; @@ -1759,7 +1779,7 @@ impl<'a> Parser<'a> { } match ty { Ok(ty) => { - let ident = Ident::new(kw::Invalid, self.prev_token.span); + let ident = Ident::new(kw::Empty, self.prev_token.span); let bm = BindingMode::ByValue(Mutability::Not); let pat = self.mk_pat_ident(ty.span, bm, ident); (pat, ty) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index e19ebb8fd2..e2af63d174 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -12,7 +12,6 @@ mod ty; use crate::lexer::UnmatchedBrace; pub use diagnostics::AttemptLocalParseRecovery; use diagnostics::Error; -pub use pat::OrPatNonterminalMode; pub use path::PathStyle; use rustc_ast::ptr::P; @@ -20,8 +19,8 @@ use rustc_ast::token::{self, DelimToken, Token, TokenKind}; use rustc_ast::tokenstream::{self, DelimSpan, LazyTokenStream, Spacing}; use rustc_ast::tokenstream::{CreateTokenStream, TokenStream, TokenTree, TreeAndSpacing}; use rustc_ast::DUMMY_NODE_ID; -use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe}; -use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacDelimiter, Mutability, StrLit}; +use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, CrateSugar, Extern, HasTokens}; +use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacDelimiter, Mutability, StrLit, Unsafe}; use rustc_ast::{Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; @@ -55,6 +54,18 @@ enum BlockMode { Ignore, } +/// Whether or not we should force collection of tokens for an AST node, +/// regardless of whether or not it has attributes +pub enum ForceCollect { + Yes, + No, +} + +pub enum TrailingToken { + None, + Semi, +} + /// Like `maybe_whole_expr`, but for things other than expressions. #[macro_export] macro_rules! maybe_whole { @@ -266,7 +277,7 @@ impl TokenCursor { } } -#[derive(Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] enum TokenType { Token(TokenKind), Keyword(Symbol), @@ -721,13 +732,9 @@ impl<'a> Parser<'a> { Ok(t) => { // Parsed successfully, therefore most probably the code only // misses a separator. - let mut exp_span = self.sess.source_map().next_point(sp); - if self.sess.source_map().is_multiline(exp_span) { - exp_span = sp; - } expect_err .span_suggestion_short( - exp_span, + sp, &format!("missing `{}`", token_str), token_str, Applicability::MaybeIncorrect, @@ -973,12 +980,8 @@ impl<'a> Parser<'a> { } } - // The value here is never passed to macros as tokens by itself (not as a part - // of the whole attribute), so we don't collect tokens here. If this changes, - // then token will need to be collected. One catch here is that we are using - // a nonterminal for keeping the expression, but this nonterminal should not - // be wrapped into a group when converting to token stream. - let expr = self.parse_expr()?; + // Collect tokens because they are used during lowering to HIR. + let expr = self.collect_tokens(|this| this.parse_expr())?; let span = expr.span; match &expr.kind { @@ -988,8 +991,8 @@ impl<'a> Parser<'a> { _ => self.sess.gated_spans.gate(sym::extended_key_value_attributes, span), } - let token = token::Interpolated(Lrc::new(token::NtExpr(expr))); - MacArgs::Eq(eq_span, TokenTree::token(token, span).into()) + let token_kind = token::Interpolated(Lrc::new(token::NtExpr(expr))); + MacArgs::Eq(eq_span, Token::new(token_kind, span)) } else { MacArgs::Empty } @@ -1223,6 +1226,13 @@ impl<'a> Parser<'a> { } } + pub fn collect_tokens( + &mut self, + f: impl FnOnce(&mut Self) -> PResult<'a, R>, + ) -> PResult<'a, R> { + self.collect_tokens_trailing_token(|this| Ok((f(this)?, TrailingToken::None))) + } + /// Records all tokens consumed by the provided callback, /// including the current token. These tokens are collected /// into a `LazyTokenStream`, and returned along with the result @@ -1239,14 +1249,14 @@ impl<'a> Parser<'a> { /// This restriction shouldn't be an issue in practice, /// since this function is used to record the tokens for /// a parsed AST item, which always has matching delimiters. - pub fn collect_tokens( + pub fn collect_tokens_trailing_token( &mut self, - f: impl FnOnce(&mut Self) -> PResult<'a, R>, - ) -> PResult<'a, (R, Option)> { + f: impl FnOnce(&mut Self) -> PResult<'a, (R, TrailingToken)>, + ) -> PResult<'a, R> { let start_token = (self.token.clone(), self.token_spacing); let cursor_snapshot = self.token_cursor.clone(); - let ret = f(self)?; + let (mut ret, trailing_token) = f(self)?; // Produces a `TokenStream` on-demand. Using `cursor_snapshot` // and `num_calls`, we can reconstruct the `TokenStream` seen @@ -1265,15 +1275,10 @@ impl<'a> Parser<'a> { cursor_snapshot: TokenCursor, num_calls: usize, desugar_doc_comments: bool, - trailing_semi: bool, append_unglued_token: Option, } impl CreateTokenStream for LazyTokenStreamImpl { fn create_token_stream(&self) -> TokenStream { - let mut num_calls = self.num_calls; - if self.trailing_semi { - num_calls += 1; - } // The token produced by the final call to `next` or `next_desugared` // was not actually consumed by the callback. The combination // of chaining the initial token and using `take` produces the desired @@ -1281,42 +1286,37 @@ impl<'a> Parser<'a> { // and omit the final token otherwise. let mut cursor_snapshot = self.cursor_snapshot.clone(); let tokens = std::iter::once(self.start_token.clone()) - .chain((0..num_calls).map(|_| { + .chain((0..self.num_calls).map(|_| { if self.desugar_doc_comments { cursor_snapshot.next_desugared() } else { cursor_snapshot.next() } })) - .take(num_calls); + .take(self.num_calls); make_token_stream(tokens, self.append_unglued_token.clone()) } - fn add_trailing_semi(&self) -> Box { - if self.trailing_semi { - panic!("Called `add_trailing_semi` twice!"); - } - if self.append_unglued_token.is_some() { - panic!( - "Cannot call `add_trailing_semi` when we have an unglued token {:?}", - self.append_unglued_token - ); - } - let mut new = self.clone(); - new.trailing_semi = true; - Box::new(new) + } + + let mut num_calls = self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls; + match trailing_token { + TrailingToken::None => {} + TrailingToken::Semi => { + assert_eq!(self.token.kind, token::Semi); + num_calls += 1; } } let lazy_impl = LazyTokenStreamImpl { start_token, - num_calls: self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls, + num_calls, cursor_snapshot, desugar_doc_comments: self.desugar_doc_comments, - trailing_semi: false, append_unglued_token: self.token_cursor.append_unglued_token.clone(), }; - Ok((ret, Some(LazyTokenStream::new(lazy_impl)))) + ret.finalize_tokens(LazyTokenStream::new(lazy_impl)); + Ok(ret) } /// `::{` or `::*` @@ -1409,3 +1409,16 @@ fn make_token_stream( assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack); TokenStream::new(final_buf.inner) } + +#[macro_export] +macro_rules! maybe_collect_tokens { + ($self:ident, $force_collect:expr, $attrs:expr, $f:expr) => { + if matches!($force_collect, ForceCollect::Yes) + || $crate::parser::attr::maybe_needs_tokens($attrs) + { + $self.collect_tokens_trailing_token($f) + } else { + Ok($f($self)?.0) + } + }; +} diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index a6b9ac1014..6e25209f09 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -4,19 +4,15 @@ use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_span::symbol::{kw, Ident}; -use crate::parser::pat::{GateOr, OrPatNonterminalMode, RecoverComma}; -use crate::parser::{FollowedByType, Parser, PathStyle}; +use crate::parser::pat::{GateOr, RecoverComma}; +use crate::parser::{FollowedByType, ForceCollect, Parser, PathStyle}; impl<'a> Parser<'a> { /// Checks whether a non-terminal may begin with a particular token. /// /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that /// token. Be conservative (return true) if not sure. - pub fn nonterminal_may_begin_with( - kind: NonterminalKind, - token: &Token, - or_pat_mode: OrPatNonterminalMode, - ) -> bool { + pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool { /// Checks whether the non-terminal may contain a single (non-keyword) identifier. fn may_be_ident(nt: &token::Nonterminal) -> bool { match *nt { @@ -45,13 +41,16 @@ impl<'a> Parser<'a> { }, NonterminalKind::Block => match token.kind { token::OpenDelim(token::Brace) => true, - token::Interpolated(ref nt) => !matches!(**nt, token::NtItem(_) - | token::NtPat(_) - | token::NtTy(_) - | token::NtIdent(..) - | token::NtMeta(_) - | token::NtPath(_) - | token::NtVis(_)), + token::Interpolated(ref nt) => !matches!( + **nt, + token::NtItem(_) + | token::NtPat(_) + | token::NtTy(_) + | token::NtIdent(..) + | token::NtMeta(_) + | token::NtPath(_) + | token::NtVis(_) + ), _ => false, }, NonterminalKind::Path | NonterminalKind::Meta => match token.kind { @@ -62,7 +61,7 @@ impl<'a> Parser<'a> { }, _ => false, }, - NonterminalKind::Pat => match token.kind { + NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => match token.kind { token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) token::OpenDelim(token::Paren) | // tuple pattern token::OpenDelim(token::Bracket) | // slice pattern @@ -76,7 +75,7 @@ impl<'a> Parser<'a> { token::Lt | // path (UFCS constant) token::BinOp(token::Shl) => true, // path (double UFCS) // leading vert `|` or-pattern - token::BinOp(token::Or) => matches!(or_pat_mode, OrPatNonterminalMode::TopPat), + token::BinOp(token::Or) => matches!(kind, NonterminalKind::Pat2021 {..}), token::Interpolated(ref nt) => may_be_ident(nt), _ => false, }, @@ -94,11 +93,7 @@ impl<'a> Parser<'a> { } /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). - pub fn parse_nonterminal( - &mut self, - kind: NonterminalKind, - or_pat_mode: OrPatNonterminalMode, - ) -> PResult<'a, Nonterminal> { + pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, Nonterminal> { // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`) // needs to have them force-captured here. // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, @@ -106,80 +101,35 @@ impl<'a> Parser<'a> { // in advance whether or not a proc-macro will be (transitively) invoked, // we always capture tokens for any `Nonterminal` which needs them. Ok(match kind { - NonterminalKind::Item => match self.collect_tokens(|this| this.parse_item())? { - (Some(mut item), tokens) => { - // If we captured tokens during parsing (due to outer attributes), - // use those. - if item.tokens.is_none() { - item.tokens = tokens; - } - token::NtItem(item) - } - (None, _) => { + NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? { + Some(item) => token::NtItem(item), + None => { return Err(self.struct_span_err(self.token.span, "expected an item keyword")); } }, NonterminalKind::Block => { - let (mut block, tokens) = self.collect_tokens(|this| this.parse_block())?; - // We have have eaten an NtBlock, which could already have tokens - if block.tokens.is_none() { - block.tokens = tokens; - } - token::NtBlock(block) + token::NtBlock(self.collect_tokens(|this| this.parse_block())?) } - NonterminalKind::Stmt => { - let (stmt, tokens) = self.collect_tokens(|this| this.parse_stmt())?; - match stmt { - Some(mut s) => { - if s.tokens().is_none() { - s.set_tokens(tokens); - } - token::NtStmt(s) - } - None => { - return Err(self.struct_span_err(self.token.span, "expected a statement")); - } + NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? { + Some(s) => token::NtStmt(s), + None => { + return Err(self.struct_span_err(self.token.span, "expected a statement")); } - } - NonterminalKind::Pat => { - let (mut pat, tokens) = self.collect_tokens(|this| match or_pat_mode { - OrPatNonterminalMode::TopPat => { + }, + NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => { + token::NtPat(self.collect_tokens(|this| match kind { + NonterminalKind::Pat2018 { .. } => this.parse_pat(None), + NonterminalKind::Pat2021 { .. } => { this.parse_top_pat(GateOr::Yes, RecoverComma::No) } - OrPatNonterminalMode::NoTopAlt => this.parse_pat(None), - })?; - // We have have eaten an NtPat, which could already have tokens - if pat.tokens.is_none() { - pat.tokens = tokens; - } - token::NtPat(pat) - } - NonterminalKind::Expr => { - let (mut expr, tokens) = self.collect_tokens(|this| this.parse_expr())?; - // If we captured tokens during parsing (due to outer attributes), - // use those. - if expr.tokens.is_none() { - expr.tokens = tokens; - } - token::NtExpr(expr) + _ => unreachable!(), + })?) } + NonterminalKind::Expr => token::NtExpr(self.collect_tokens(|this| this.parse_expr())?), NonterminalKind::Literal => { - let (mut lit, tokens) = - self.collect_tokens(|this| this.parse_literal_maybe_minus())?; - // We have have eaten a nonterminal, which could already have tokens - if lit.tokens.is_none() { - lit.tokens = tokens; - } - token::NtLiteral(lit) - } - NonterminalKind::Ty => { - let (mut ty, tokens) = self.collect_tokens(|this| this.parse_ty())?; - // We have an eaten an NtTy, which could already have tokens - if ty.tokens.is_none() { - ty.tokens = tokens; - } - token::NtTy(ty) + token::NtLiteral(self.collect_tokens(|this| this.parse_literal_maybe_minus())?) } + NonterminalKind::Ty => token::NtTy(self.collect_tokens(|this| this.parse_ty())?), // this could be handled like a token, since it is one NonterminalKind::Ident => { if let Some((ident, is_raw)) = get_macro_ident(&self.token) { @@ -192,32 +142,15 @@ impl<'a> Parser<'a> { } } NonterminalKind::Path => { - let (mut path, tokens) = - self.collect_tokens(|this| this.parse_path(PathStyle::Type))?; - // We have have eaten an NtPath, which could already have tokens - if path.tokens.is_none() { - path.tokens = tokens; - } - token::NtPath(path) + token::NtPath(self.collect_tokens(|this| this.parse_path(PathStyle::Type))?) } NonterminalKind::Meta => { - let (mut attr, tokens) = self.collect_tokens(|this| this.parse_attr_item(false))?; - // We may have eaten a nonterminal, which could already have tokens - if attr.tokens.is_none() { - attr.tokens = tokens; - } - token::NtMeta(P(attr)) + token::NtMeta(P(self.collect_tokens(|this| this.parse_attr_item(false))?)) } NonterminalKind::TT => token::NtTT(self.parse_token_tree()), - NonterminalKind::Vis => { - let (mut vis, tokens) = - self.collect_tokens(|this| this.parse_visibility(FollowedByType::Yes))?; - // We may have etan an `NtVis`, which could already have tokens - if vis.tokens.is_none() { - vis.tokens = tokens; - } - token::NtVis(vis) - } + NonterminalKind::Vis => token::NtVis( + self.collect_tokens(|this| this.parse_visibility(FollowedByType::Yes))?, + ), NonterminalKind::Lifetime => { if self.check_lifetime() { token::NtLifetime(self.expect_lifetime().ident) diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 1da371e0b7..d888514cf5 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -31,13 +31,6 @@ pub(super) enum RecoverComma { No, } -/// Used when parsing a non-terminal (see `parse_nonterminal`) to determine if `:pat` should match -/// `top_pat` or `pat`. See issue . -pub enum OrPatNonterminalMode { - TopPat, - NoTopAlt, -} - impl<'a> Parser<'a> { /// Parses a pattern. /// @@ -247,7 +240,7 @@ impl<'a> Parser<'a> { Err(err) } - /// Parse and throw away a parentesized comma separated + /// Parse and throw away a parenthesized comma separated /// sequence of patterns until `)` is reached. fn skip_pat_list(&mut self) -> PResult<'a, ()> { while !self.check(&token::CloseDelim(token::Paren)) { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 4510e86e03..6b7059eecf 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -133,7 +133,15 @@ impl<'a> Parser<'a> { maybe_whole!(self, NtPath, |path| { if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some()) { - self.struct_span_err(path.span, "unexpected generic arguments in path").emit(); + self.struct_span_err( + path.segments + .iter() + .filter_map(|segment| segment.args.as_ref()) + .map(|arg| arg.span()) + .collect::>(), + "unexpected generic arguments in path", + ) + .emit(); } path }); @@ -185,7 +193,6 @@ impl<'a> Parser<'a> { pub(super) fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> { let ident = self.parse_path_segment_ident()?; - let is_args_start = |token: &Token| { matches!( token.kind, @@ -230,10 +237,11 @@ impl<'a> Parser<'a> { } else { // `(T, U) -> R` let (inputs, _) = self.parse_paren_comma_seq(|p| p.parse_ty())?; + let inputs_span = lo.to(self.prev_token.span); let span = ident.span.to(self.prev_token.span); let output = self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?; - ParenthesizedArgs { inputs, output, span }.into() + ParenthesizedArgs { span, inputs, inputs_span, output }.into() }; PathSegment { ident, args, id: ast::DUMMY_NODE_ID } @@ -419,7 +427,10 @@ impl<'a> Parser<'a> { match arg { Some(arg) => { if self.check(&token::Colon) | self.check(&token::Eq) { - let (ident, gen_args) = self.get_ident_from_generic_arg(arg, lo)?; + let (ident, gen_args) = match self.get_ident_from_generic_arg(arg) { + Ok(ident_gen_args) => ident_gen_args, + Err(arg) => return Ok(Some(AngleBracketedArg::Arg(arg))), + }; let kind = if self.eat(&token::Colon) { // Parse associated type constraint bound. @@ -501,10 +512,9 @@ impl<'a> Parser<'a> { pub(super) fn expr_is_valid_const_arg(&self, expr: &P) -> bool { match &expr.kind { ast::ExprKind::Block(_, _) | ast::ExprKind::Lit(_) => true, - ast::ExprKind::Unary(ast::UnOp::Neg, expr) => match &expr.kind { - ast::ExprKind::Lit(_) => true, - _ => false, - }, + ast::ExprKind::Unary(ast::UnOp::Neg, expr) => { + matches!(expr.kind, ast::ExprKind::Lit(_)) + } // We can only resolve single-segment paths at the moment, because multi-segment paths // require type-checking: see `visit_generic_arg` in `src/librustc_resolve/late.rs`. ast::ExprKind::Path(None, path) @@ -516,6 +526,23 @@ impl<'a> Parser<'a> { } } + /// Parse a const argument, e.g. `<3>`. It is assumed the angle brackets will be parsed by + /// the caller. + pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> { + // Parse const argument. + let value = if let token::OpenDelim(token::Brace) = self.token.kind { + self.parse_block_expr( + None, + self.token.span, + BlockCheckMode::Default, + ast::AttrVec::new(), + )? + } else { + self.handle_unambiguous_unbraced_const_arg()? + }; + Ok(AnonConst { id: ast::DUMMY_NODE_ID, value }) + } + /// Parse a generic argument in a path segment. /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`. fn parse_generic_arg(&mut self) -> PResult<'a, Option> { @@ -525,17 +552,7 @@ impl<'a> Parser<'a> { GenericArg::Lifetime(self.expect_lifetime()) } else if self.check_const_arg() { // Parse const argument. - let value = if let token::OpenDelim(token::Brace) = self.token.kind { - self.parse_block_expr( - None, - self.token.span, - BlockCheckMode::Default, - ast::AttrVec::new(), - )? - } else { - self.handle_unambiguous_unbraced_const_arg()? - }; - GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) + GenericArg::Const(self.parse_const_arg()?) } else if self.check_type() { // Parse type argument. match self.parse_ty() { @@ -554,50 +571,15 @@ impl<'a> Parser<'a> { fn get_ident_from_generic_arg( &self, gen_arg: GenericArg, - lo: Span, - ) -> PResult<'a, (Ident, Option)> { - let gen_arg_span = gen_arg.span(); - match gen_arg { - GenericArg::Type(t) => match t.into_inner().kind { - ast::TyKind::Path(qself, mut path) => { - if let Some(qself) = qself { - let mut err = self.struct_span_err( - gen_arg_span, - "qualified paths cannot be used in associated type constraints", - ); - err.span_label( - qself.path_span, - "not allowed in associated type constraints", - ); - return Err(err); - } - if path.segments.len() == 1 { - let path_seg = path.segments.remove(0); - let ident = path_seg.ident; - let gen_args = path_seg.args.map(|args| args.into_inner()); - return Ok((ident, gen_args)); - } - let err = self.struct_span_err( - path.span, - "paths with multiple segments cannot be used in associated type constraints", - ); - return Err(err); - } - _ => { - let span = lo.to(self.prev_token.span); - let err = self.struct_span_err( - span, - "only path types can be used in associated type constraints", - ); - return Err(err); + ) -> Result<(Ident, Option), GenericArg> { + if let GenericArg::Type(ty) = &gen_arg { + if let ast::TyKind::Path(qself, path) = &ty.kind { + if qself.is_none() && path.segments.len() == 1 { + let seg = &path.segments[0]; + return Ok((seg.ident, seg.args.as_deref().cloned())); } - }, - _ => { - let span = lo.to(self.prev_token.span); - let err = self - .struct_span_err(span, "only types can be used in associated type constraints"); - return Err(err); } } + Err(gen_arg) } } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 2942747991..8373f6acd7 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -3,14 +3,13 @@ use super::diagnostics::{AttemptLocalParseRecovery, Error}; use super::expr::LhsExpr; use super::pat::{GateOr, RecoverComma}; use super::path::PathStyle; -use super::{BlockMode, Parser, Restrictions, SemiColonMode}; -use crate::maybe_whole; +use super::{BlockMode, ForceCollect, Parser, Restrictions, SemiColonMode, TrailingToken}; +use crate::{maybe_collect_tokens, maybe_whole}; use rustc_ast as ast; use rustc_ast::attr::HasAttrs; use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; -use rustc_ast::tokenstream::LazyTokenStream; use rustc_ast::util::classify; use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacCallStmt, MacStmtStyle}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKind, DUMMY_NODE_ID}; @@ -24,17 +23,22 @@ impl<'a> Parser<'a> { /// Parses a statement. This stops just before trailing semicolons on everything but items. /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. // Public for rustfmt usage. - pub fn parse_stmt(&mut self) -> PResult<'a, Option> { - Ok(self.parse_stmt_without_recovery().unwrap_or_else(|mut e| { + pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option> { + Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|mut e| { e.emit(); self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); None })) } - fn parse_stmt_without_recovery(&mut self) -> PResult<'a, Option> { + /// If `force_capture` is true, forces collection of tokens regardless of whether + /// or not we have attributes + fn parse_stmt_without_recovery( + &mut self, + capture_semi: bool, + force_collect: ForceCollect, + ) -> PResult<'a, Option> { let mut attrs = self.parse_outer_attributes()?; - let has_attrs = !attrs.is_empty(); let lo = self.token.span; maybe_whole!(self, NtStmt, |stmt| { @@ -46,83 +50,77 @@ impl<'a> Parser<'a> { Some(stmt) }); - let parse_stmt_inner = |this: &mut Self| { - let stmt = if this.eat_keyword(kw::Let) { - this.parse_local_mk(lo, attrs.into())? - } else if this.is_kw_followed_by_ident(kw::Mut) { - this.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut")? - } else if this.is_kw_followed_by_ident(kw::Auto) { - this.bump(); // `auto` - let msg = "write `let` instead of `auto` to introduce a new variable"; - this.recover_stmt_local(lo, attrs.into(), msg, "let")? - } else if this.is_kw_followed_by_ident(sym::var) { - this.bump(); // `var` - let msg = "write `let` instead of `var` to introduce a new variable"; - this.recover_stmt_local(lo, attrs.into(), msg, "let")? - } else if this.check_path() - && !this.token.is_qpath_start() - && !this.is_path_start_item() - { - // We have avoided contextual keywords like `union`, items with `crate` visibility, - // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something - // that starts like a path (1 token), but it fact not a path. - // Also, we avoid stealing syntax from `parse_item_`. - this.parse_stmt_path_start(lo, attrs)? - } else if let Some(item) = - this.parse_item_common(attrs.clone(), false, true, |_| true)? - { - // FIXME: Bad copy of attrs - this.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) - } else if this.eat(&token::Semi) { - // Do not attempt to parse an expression if we're done here. - this.error_outer_attrs(&attrs); - this.mk_stmt(lo, StmtKind::Empty) - } else if this.token != token::CloseDelim(token::Brace) { - // Remainder are line-expr stmts. - let e = this.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?; - this.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) - } else { - this.error_outer_attrs(&attrs); - return Ok(None); - }; - Ok(Some(stmt)) - }; - - let stmt = if has_attrs { - let (mut stmt, tokens) = self.collect_tokens(parse_stmt_inner)?; - if let Some(stmt) = &mut stmt { - // If we already have tokens (e.g. due to encounting an `NtStmt`), - // use those instead. - if stmt.tokens().is_none() { - stmt.set_tokens(tokens); - } - } - stmt + Ok(Some(if self.token.is_keyword(kw::Let) { + self.parse_local_mk(lo, attrs.into(), capture_semi, force_collect)? + } else if self.is_kw_followed_by_ident(kw::Mut) { + self.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut")? + } else if self.is_kw_followed_by_ident(kw::Auto) { + self.bump(); // `auto` + let msg = "write `let` instead of `auto` to introduce a new variable"; + self.recover_stmt_local(lo, attrs.into(), msg, "let")? + } else if self.is_kw_followed_by_ident(sym::var) { + self.bump(); // `var` + let msg = "write `let` instead of `var` to introduce a new variable"; + self.recover_stmt_local(lo, attrs.into(), msg, "let")? + } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { + // We have avoided contextual keywords like `union`, items with `crate` visibility, + // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something + // that starts like a path (1 token), but it fact not a path. + // Also, we avoid stealing syntax from `parse_item_`. + self.parse_stmt_path_start(lo, attrs, force_collect)? + } else if let Some(item) = + self.parse_item_common(attrs.clone(), false, true, |_| true, force_collect)? + { + // FIXME: Bad copy of attrs + self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) + } else if self.eat(&token::Semi) { + // Do not attempt to parse an expression if we're done here. + self.error_outer_attrs(&attrs); + self.mk_stmt(lo, StmtKind::Empty) + } else if self.token != token::CloseDelim(token::Brace) { + // Remainder are line-expr stmts. + let e = self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?; + self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) } else { - parse_stmt_inner(self)? - }; - Ok(stmt) + self.error_outer_attrs(&attrs); + return Ok(None); + })) } - fn parse_stmt_path_start(&mut self, lo: Span, attrs: Vec) -> PResult<'a, Stmt> { - let path = self.parse_path(PathStyle::Expr)?; + fn parse_stmt_path_start( + &mut self, + lo: Span, + attrs: Vec, + force_collect: ForceCollect, + ) -> PResult<'a, Stmt> { + maybe_collect_tokens!(self, force_collect, &attrs, |this: &mut Parser<'a>| { + let path = this.parse_path(PathStyle::Expr)?; - if self.eat(&token::Not) { - return self.parse_stmt_mac(lo, attrs.into(), path); - } + if this.eat(&token::Not) { + let stmt_mac = this.parse_stmt_mac(lo, attrs.into(), path)?; + if this.token == token::Semi { + return Ok((stmt_mac, TrailingToken::Semi)); + } else { + return Ok((stmt_mac, TrailingToken::None)); + } + } - let expr = if self.eat(&token::OpenDelim(token::Brace)) { - self.parse_struct_expr(path, AttrVec::new(), true)? - } else { - let hi = self.prev_token.span; - self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) - }; + let expr = if this.eat(&token::OpenDelim(token::Brace)) { + this.parse_struct_expr(path, AttrVec::new(), true)? + } else { + let hi = this.prev_token.span; + this.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) + }; - let expr = self.with_res(Restrictions::STMT_EXPR, |this| { - let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?; - this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) - })?; - Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) + let expr = this.with_res(Restrictions::STMT_EXPR, |this| { + let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?; + this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) + })?; + Ok(( + this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Expr(expr)), + TrailingToken::None, + )) + }) } /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`. @@ -170,15 +168,34 @@ impl<'a> Parser<'a> { msg: &str, sugg: &str, ) -> PResult<'a, Stmt> { - let stmt = self.parse_local_mk(lo, attrs)?; + let stmt = self.recover_local_after_let(lo, attrs)?; self.struct_span_err(lo, "invalid variable declaration") .span_suggestion(lo, msg, sugg.to_string(), Applicability::MachineApplicable) .emit(); Ok(stmt) } - fn parse_local_mk(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { - let local = self.parse_local(attrs)?; + fn parse_local_mk( + &mut self, + lo: Span, + attrs: AttrVec, + capture_semi: bool, + force_collect: ForceCollect, + ) -> PResult<'a, Stmt> { + maybe_collect_tokens!(self, force_collect, &attrs, |this: &mut Parser<'a>| { + this.expect_keyword(kw::Let)?; + let local = this.parse_local(attrs.into())?; + let trailing = if capture_semi && this.token.kind == token::Semi { + TrailingToken::Semi + } else { + TrailingToken::None + }; + Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), trailing)) + }) + } + + fn recover_local_after_let(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { + let local = self.parse_local(attrs.into())?; Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Local(local))) } @@ -300,7 +317,7 @@ impl<'a> Parser<'a> { // bar; // // which is valid in other languages, but not Rust. - match self.parse_stmt_without_recovery() { + match self.parse_stmt_without_recovery(false, ForceCollect::No) { // If the next token is an open brace (e.g., `if a b {`), the place- // inside-a-block suggestion would be more likely wrong than right. Ok(Some(_)) @@ -403,17 +420,11 @@ impl<'a> Parser<'a> { // Skip looking for a trailing semicolon when we have an interpolated statement. maybe_whole!(self, NtStmt, |x| Some(x)); - let mut stmt = match self.parse_stmt_without_recovery()? { + let mut stmt = match self.parse_stmt_without_recovery(true, ForceCollect::No)? { Some(stmt) => stmt, None => return Ok(None), }; - let add_semi_token = |tokens: Option<&mut LazyTokenStream>| { - if let Some(tokens) = tokens { - *tokens = tokens.add_trailing_semi(); - } - }; - let mut eat_semi = true; match stmt.kind { // Expression without semicolon. @@ -469,18 +480,12 @@ impl<'a> Parser<'a> { } } eat_semi = false; - // We just checked that there's a semicolon in the tokenstream, - // so capture it - add_semi_token(local.tokens.as_mut()); } StmtKind::Empty | StmtKind::Item(_) | StmtKind::Semi(_) => eat_semi = false, } if eat_semi && self.eat(&token::Semi) { stmt = stmt.add_trailing_semicolon(); - // We just checked that we have a semicolon in the tokenstream, - // so capture it - add_semi_token(stmt.tokens_mut()); } stmt.span = stmt.span.to(self.prev_token.span); Ok(Some(stmt)) diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index f4bb961094..21372725a6 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -2,7 +2,7 @@ use crate::parse_in; -use rustc_ast::tokenstream::DelimSpan; +use rustc_ast::tokenstream::{DelimSpan, TokenTree}; use rustc_ast::{self as ast, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind}; use rustc_errors::{Applicability, PResult}; use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP}; @@ -45,7 +45,8 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta kind: match &item.args { MacArgs::Empty => MetaItemKind::Word, MacArgs::Eq(_, t) => { - let v = parse_in(sess, t.clone(), "name value", |p| p.parse_unsuffixed_lit())?; + let t = TokenTree::Token(t.clone()).into(); + let v = parse_in(sess, t, "name value", |p| p.parse_unsuffixed_lit())?; MetaItemKind::NameValue(v) } MacArgs::Delimited(dspan, delim, t) => { diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 25e3e67e28..f150f7a41a 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -347,7 +347,7 @@ impl<'a> Parser<'a> { let mut pos = pos; // This handles the raw string case, the raw argument is the number of # // in r###"..."### (we need to add one because of the `r`). - let raw = self.style.map(|raw| raw + 1).unwrap_or(0); + let raw = self.style.map_or(0, |raw| raw + 1); for skip in &self.skips { if pos > *skip { pos += 1; @@ -736,7 +736,7 @@ fn find_skips_from_snippet( fn find_skips(snippet: &str, is_raw: bool) -> Vec { let mut eat_ws = false; - let mut s = snippet.chars().enumerate().peekable(); + let mut s = snippet.char_indices().peekable(); let mut skips = vec![]; while let Some((pos, c)) = s.next() { match (c, s.peek()) { @@ -814,7 +814,7 @@ fn find_skips_from_snippet( skips } - let r_start = str_style.map(|r| r + 1).unwrap_or(0); + let r_start = str_style.map_or(0, |r| r + 1); let r_end = str_style.unwrap_or(0); let s = &snippet[r_start + 1..snippet.len() - r_end - 1]; (find_skips(s, str_style.is_some()), true) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index aeaa862f5f..0e3a722e08 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -32,7 +32,7 @@ pub(crate) fn target_from_impl_item<'tcx>( let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id); let containing_item = tcx.hir().expect_item(parent_hir_id); let containing_impl_is_for_trait = match &containing_item.kind { - hir::ItemKind::Impl { ref of_trait, .. } => of_trait.is_some(), + hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(), _ => bug!("parent of an ImplItem must be an Impl"), }; if containing_impl_is_for_trait { @@ -70,27 +70,27 @@ impl CheckAttrVisitor<'tcx> { is_valid &= if self.tcx.sess.check_name(attr, sym::inline) { self.check_inline(hir_id, attr, span, target) } else if self.tcx.sess.check_name(attr, sym::non_exhaustive) { - self.check_non_exhaustive(attr, span, target) + self.check_non_exhaustive(hir_id, attr, span, target) } else if self.tcx.sess.check_name(attr, sym::marker) { - self.check_marker(attr, span, target) + self.check_marker(hir_id, attr, span, target) } else if self.tcx.sess.check_name(attr, sym::target_feature) { self.check_target_feature(hir_id, attr, span, target) } else if self.tcx.sess.check_name(attr, sym::track_caller) { - self.check_track_caller(&attr.span, attrs, span, target) + self.check_track_caller(hir_id, &attr.span, attrs, span, target) } else if self.tcx.sess.check_name(attr, sym::doc) { self.check_doc_attrs(attr, hir_id, target) } else if self.tcx.sess.check_name(attr, sym::no_link) { - self.check_no_link(&attr, span, target) + self.check_no_link(hir_id, &attr, span, target) } else if self.tcx.sess.check_name(attr, sym::export_name) { - self.check_export_name(&attr, span, target) + self.check_export_name(hir_id, &attr, span, target) } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) { self.check_rustc_args_required_const(&attr, span, target, item) } else if self.tcx.sess.check_name(attr, sym::allow_internal_unstable) { - self.check_allow_internal_unstable(&attr, span, target, &attrs) + self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs) } else if self.tcx.sess.check_name(attr, sym::rustc_allow_const_fn_unstable) { self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target) } else if self.tcx.sess.check_name(attr, sym::naked) { - self.check_naked(attr, span, target) + self.check_naked(hir_id, attr, span, target) } else { // lint-only checks if self.tcx.sess.check_name(attr, sym::cold) { @@ -118,6 +118,41 @@ impl CheckAttrVisitor<'tcx> { self.check_used(attrs, target); } + fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build(&format!( + "`#[{}]` is ignored on struct fields, match arms and macro defs", + sym, + )) + .warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ) + .note( + "see issue #80564 \ + for more information", + ) + .emit(); + }); + } + + fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build(&format!("`#[{}]` is ignored on struct fields and match arms", sym)) + .warn( + "this was previously accepted by the compiler but is \ + being phased out; it will become a hard error in \ + a future release!", + ) + .note( + "see issue #80564 \ + for more information", + ) + .emit(); + }); + } + /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool { match target { @@ -150,6 +185,11 @@ impl CheckAttrVisitor<'tcx> { }); true } + // FIXME(#80564): Same for fields, arms, and macro defs + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline"); + true + } _ => { struct_span_err!( self.tcx.sess, @@ -165,10 +205,18 @@ impl CheckAttrVisitor<'tcx> { } /// Checks if `#[naked]` is applied to a function definition. - fn check_naked(&self, attr: &Attribute, span: &Span, target: Target) -> bool { + fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool { match target { Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[allow_internal_unstable]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked"); + true + } _ => { self.tcx .sess @@ -186,6 +234,7 @@ impl CheckAttrVisitor<'tcx> { /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid. fn check_track_caller( &self, + hir_id: HirId, attr_span: &Span, attrs: &'hir [Attribute], span: &Span, @@ -203,6 +252,16 @@ impl CheckAttrVisitor<'tcx> { false } Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true, + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[track_caller]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + for attr in attrs { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller"); + } + true + } _ => { struct_span_err!( self.tcx.sess, @@ -218,9 +277,23 @@ impl CheckAttrVisitor<'tcx> { } /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid. - fn check_non_exhaustive(&self, attr: &Attribute, span: &Span, target: Target) -> bool { + fn check_non_exhaustive( + &self, + hir_id: HirId, + attr: &Attribute, + span: &Span, + target: Target, + ) -> bool { match target { Target::Struct | Target::Enum | Target::Variant => true, + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[non_exhaustive]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive"); + true + } _ => { struct_span_err!( self.tcx.sess, @@ -236,9 +309,17 @@ impl CheckAttrVisitor<'tcx> { } /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid. - fn check_marker(&self, attr: &Attribute, span: &Span, target: Target) -> bool { + fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool { match target { Target::Trait => true, + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[marker]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker"); + true + } _ => { self.tcx .sess @@ -276,6 +357,14 @@ impl CheckAttrVisitor<'tcx> { }); true } + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[target_feature]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature"); + true + } _ => { self.tcx .sess @@ -310,7 +399,7 @@ impl CheckAttrVisitor<'tcx> { .sess .struct_span_err( meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - &format!("{:?} character isn't allowed in `#[doc(alias = \"...\")]`", c,), + &format!("{:?} character isn't allowed in `#[doc(alias = \"...\")]`", c), ) .emit(); return false; @@ -343,7 +432,7 @@ impl CheckAttrVisitor<'tcx> { // We can't link to trait impl's consts. let err = "associated constant in trait implementation block"; match containing_item.kind { - ItemKind::Impl { of_trait: Some(_), .. } => Some(err), + ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err), _ => None, } } @@ -358,6 +447,17 @@ impl CheckAttrVisitor<'tcx> { .emit(); return false; } + let item_name = self.tcx.hir().name(hir_id); + if &*item_name.as_str() == doc_alias { + self.tcx + .sess + .struct_span_err( + meta.span(), + &format!("`#[doc(alias = \"...\")]` is the same as the item's name"), + ) + .emit(); + return false; + } true } @@ -453,6 +553,13 @@ impl CheckAttrVisitor<'tcx> { fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { match target { Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {} + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[cold]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold"); + } _ => { // FIXME: #[cold] was previously allowed on non-functions and some crates used // this, so only emit a warning. @@ -474,6 +581,13 @@ impl CheckAttrVisitor<'tcx> { fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { match target { Target::ForeignFn | Target::ForeignStatic => {} + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[link_name]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name"); + } _ => { // FIXME: #[cold] was previously allowed on non-functions/statics and some crates // used this, so only emit a warning. @@ -506,23 +620,49 @@ impl CheckAttrVisitor<'tcx> { } /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid. - fn check_no_link(&self, attr: &Attribute, span: &Span, target: Target) -> bool { - if target == Target::ExternCrate { - true - } else { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to an `extern crate` item") - .span_label(*span, "not an `extern crate` item") - .emit(); - false + fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool { + match target { + Target::ExternCrate => true, + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[no_link]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link"); + true + } + _ => { + self.tcx + .sess + .struct_span_err( + attr.span, + "attribute should be applied to an `extern crate` item", + ) + .span_label(*span, "not an `extern crate` item") + .emit(); + false + } } } /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid. - fn check_export_name(&self, attr: &Attribute, span: &Span, target: Target) -> bool { + fn check_export_name( + &self, + hir_id: HirId, + attr: &Attribute, + span: &Span, + target: Target, + ) -> bool { match target { Target::Static | Target::Fn | Target::Method(..) => true, + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[export_name]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name"); + true + } _ => { self.tcx .sess @@ -614,6 +754,13 @@ impl CheckAttrVisitor<'tcx> { fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { match target { Target::Static | Target::Fn | Target::Method(..) => {} + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[link_section]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section"); + } _ => { // FIXME: #[link_section] was previously allowed on non-functions/statics and some // crates used this, so only emit a warning. @@ -635,6 +782,13 @@ impl CheckAttrVisitor<'tcx> { fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) { match target { Target::Static | Target::Fn | Target::Method(..) => {} + // FIXME(#80564): We permit struct fields, match arms and macro defs to have an + // `#[no_mangle]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle"); + } _ => { // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some // crates used this, so only emit a warning. @@ -817,27 +971,46 @@ impl CheckAttrVisitor<'tcx> { /// (Allows proc_macro functions) fn check_allow_internal_unstable( &self, + hir_id: HirId, attr: &Attribute, span: &Span, target: Target, attrs: &[Attribute], ) -> bool { debug!("Checking target: {:?}", target); - if target == Target::Fn { - for attr in attrs { - if self.tcx.sess.is_proc_macro_attr(attr) { - debug!("Is proc macro attr"); - return true; + match target { + Target::Fn => { + for attr in attrs { + if self.tcx.sess.is_proc_macro_attr(attr) { + debug!("Is proc macro attr"); + return true; + } } + debug!("Is not proc macro attr"); + false + } + Target::MacroDef => true, + // FIXME(#80564): We permit struct fields and match arms to have an + // `#[allow_internal_unstable]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm => { + self.inline_attr_str_error_without_macro_def( + hir_id, + attr, + "allow_internal_unstable", + ); + true + } + _ => { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to a macro") + .span_label(*span, "not a macro") + .emit(); + false } - debug!("Is not proc macro attr"); } - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a macro") - .span_label(*span, "not a macro") - .emit(); - false } /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. @@ -849,17 +1022,29 @@ impl CheckAttrVisitor<'tcx> { span: &Span, target: Target, ) -> bool { - if let Target::Fn | Target::Method(_) = target { - if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) { - return true; + match target { + Target::Fn | Target::Method(_) + if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) => + { + true + } + // FIXME(#80564): We permit struct fields and match arms to have an + // `#[allow_internal_unstable]` attribute with just a lint, because we previously + // erroneously allowed it and some crates used it accidentally, to to be compatible + // with crates depending on them, we can't throw an error here. + Target::Field | Target::Arm | Target::MacroDef => { + self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable"); + true + } + _ => { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to `const fn`") + .span_label(*span, "not a `const fn`") + .emit(); + false } } - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to `const fn`") - .span_label(*span, "not a `const fn`") - .emit(); - false } } @@ -900,6 +1085,33 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { intravisit::walk_trait_item(self, trait_item) } + fn visit_struct_field(&mut self, struct_field: &'tcx hir::StructField<'tcx>) { + self.check_attributes( + struct_field.hir_id, + &struct_field.attrs, + &struct_field.span, + Target::Field, + None, + ); + intravisit::walk_struct_field(self, struct_field); + } + + fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) { + self.check_attributes(arm.hir_id, &arm.attrs, &arm.span, Target::Arm, None); + intravisit::walk_arm(self, arm); + } + + fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) { + self.check_attributes( + macro_def.hir_id, + ¯o_def.attrs, + ¯o_def.span, + Target::MacroDef, + None, + ); + intravisit::walk_macro_def(self, macro_def); + } + fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) { let target = Target::from_foreign_item(f_item); self.check_attributes( @@ -988,11 +1200,28 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { } } +fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { + for attr in attrs { + if tcx.sess.check_name(attr, sym::inline) { + struct_span_err!( + tcx.sess, + attr.span, + E0518, + "attribute should be applied to function or closure", + ) + .span_label(attr.span, "not a function or closure") + .emit(); + } + } +} + fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir() - .visit_item_likes_in_module(module_def_id, &mut CheckAttrVisitor { tcx }.as_deep_visitor()); + let check_attr_visitor = &mut CheckAttrVisitor { tcx }; + tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor()); + tcx.hir().visit_exported_macros_in_krate(check_attr_visitor); + check_invalid_macro_level_attr(tcx, tcx.hir().krate().non_exported_macro_attrs); if module_def_id.is_top_level_module() { - CheckAttrVisitor { tcx }.check_attributes( + check_attr_visitor.check_attributes( CRATE_HIR_ID, tcx.hir().krate_attrs(), &DUMMY_SP, diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 2d6bbff460..8950f9b33b 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -49,9 +49,7 @@ impl NonConstExpr { // All other expressions are allowed. Self::Loop(Loop | While | WhileLet) - | Self::Match( - WhileDesugar | WhileLetDesugar | Normal | IfDesugar { .. } | IfLetDesugar { .. }, - ) => &[], + | Self::Match(WhileDesugar | WhileLetDesugar | Normal | IfLetDesugar { .. }) => &[], }; Some(gates) @@ -201,7 +199,7 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { // Skip the following checks if we are not currently in a const context. _ if self.const_kind.is_none() => {} - hir::ExprKind::Loop(_, _, source) => { + hir::ExprKind::Loop(_, _, source, _) => { self.const_check_violated(NonConstExpr::Loop(*source), e.span); } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 00152878d6..3b1b53553d 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -23,18 +23,18 @@ use rustc_span::symbol::{sym, Symbol}; // function, then we should explore its block to check for codes that // may need to be marked as live. fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { - match tcx.hir().find(hir_id) { + matches!( + tcx.hir().find(hir_id), Some( Node::Item(..) - | Node::ImplItem(..) - | Node::ForeignItem(..) - | Node::TraitItem(..) - | Node::Variant(..) - | Node::AnonConst(..) - | Node::Pat(..), - ) => true, - _ => false, - } + | Node::ImplItem(..) + | Node::ForeignItem(..) + | Node::TraitItem(..) + | Node::Variant(..) + | Node::AnonConst(..) + | Node::Pat(..), + ) + ) } struct MarkSymbolVisitor<'tcx> { @@ -290,6 +290,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { } fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { + self.in_pat = true; match pat.kind { PatKind::Struct(ref path, ref fields, _) => { let res = self.typeck_results().qpath_res(path, pat.hir_id); @@ -302,7 +303,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { _ => (), } - self.in_pat = true; intravisit::walk_pat(self, pat); self.in_pat = false; } @@ -396,7 +396,7 @@ impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> { } } } - hir::ItemKind::Impl { ref of_trait, items, .. } => { + hir::ItemKind::Impl(hir::Impl { ref of_trait, items, .. }) => { if of_trait.is_some() { self.worklist.push(item.hir_id); } @@ -500,16 +500,16 @@ struct DeadVisitor<'tcx> { impl DeadVisitor<'tcx> { fn should_warn_about_item(&mut self, item: &hir::Item<'_>) -> bool { - let should_warn = match item.kind { + let should_warn = matches!( + item.kind, hir::ItemKind::Static(..) - | hir::ItemKind::Const(..) - | hir::ItemKind::Fn(..) - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..) => true, - _ => false, - }; + | hir::ItemKind::Const(..) + | hir::ItemKind::Fn(..) + | hir::ItemKind::TyAlias(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..) + ); should_warn && !self.symbol_is_live(item.hir_id) } diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index 711e8e87c6..0f4bb635ee 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -1,4 +1,4 @@ -use rustc_ast::{FloatTy, InlineAsmTemplatePiece, IntTy, UintTy}; +use rustc_ast::InlineAsmTemplatePiece; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -7,7 +7,7 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_index::vec::Idx; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy}; use rustc_session::lint; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Pointer, VariantIdx}; @@ -78,7 +78,7 @@ impl ExprVisitor<'tcx> { return; } - // Special-case transmutting from `typeof(function)` and + // Special-case transmuting from `typeof(function)` and // `Option` to present a clearer error. let from = unpack_option_like(self.tcx, from); if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) { diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 86ce35c6d9..c11dc231d4 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -367,10 +367,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - let is_shorthand = match param.pat.kind { - rustc_hir::PatKind::Struct(..) => true, - _ => false, - }; + let is_shorthand = matches!(param.pat.kind, rustc_hir::PatKind::Struct(..)); param.pat.each_binding(|_bm, hir_id, _x, ident| { let var = if is_shorthand { Local(LocalInfo { id: hir_id, name: ident.name, is_shorthand: true }) @@ -422,7 +419,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { } // live nodes required for interesting control flow: - hir::ExprKind::Match(..) | hir::ExprKind::Loop(..) => { + hir::ExprKind::If(..) | hir::ExprKind::Match(..) | hir::ExprKind::Loop(..) => { self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); intravisit::walk_expr(self, expr); } @@ -847,7 +844,30 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // Note that labels have been resolved, so we don't need to look // at the label ident - hir::ExprKind::Loop(ref blk, _, _) => self.propagate_through_loop(expr, &blk, succ), + hir::ExprKind::Loop(ref blk, ..) => self.propagate_through_loop(expr, &blk, succ), + + hir::ExprKind::If(ref cond, ref then, ref else_opt) => { + // + // (cond) + // | + // v + // (expr) + // / \ + // | | + // v v + // (then)(els) + // | | + // v v + // ( succ ) + // + let else_ln = + self.propagate_through_opt_expr(else_opt.as_ref().map(|e| &**e), succ); + let then_ln = self.propagate_through_expr(&then, succ); + let ln = self.live_node(expr.hir_id, expr.span); + self.init_from_succ(ln, else_ln); + self.merge_from_succ(ln, then_ln); + self.propagate_through_expr(&cond, ln) + } hir::ExprKind::Match(ref e, arms, _) => { // @@ -1339,6 +1359,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { | hir::ExprKind::Tup(..) | hir::ExprKind::Binary(..) | hir::ExprKind::Cast(..) + | hir::ExprKind::If(..) | hir::ExprKind::DropTemps(..) | hir::ExprKind::Unary(..) | hir::ExprKind::Ret(..) @@ -1385,7 +1406,7 @@ impl<'tcx> Liveness<'_, 'tcx> { fn should_warn(&self, var: Variable) -> Option { let name = self.ir.variable_name(var); - if name == kw::Invalid { + if name == kw::Empty { return None; } let name: &str = &name.as_str(); diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 9b4da71e5e..4bfac1b729 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -53,7 +53,7 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { match e.kind { - hir::ExprKind::Loop(ref b, _, source) => { + hir::ExprKind::Loop(ref b, _, source, _) => { self.with_context(Loop(source), |v| v.visit_block(&b)); } hir::ExprKind::Closure(_, ref function_decl, b, span, movability) => { @@ -68,18 +68,18 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { hir::ExprKind::Block(ref b, Some(_label)) => { self.with_context(LabeledBlock, |v| v.visit_block(&b)); } - hir::ExprKind::Break(label, ref opt_expr) => { + hir::ExprKind::Break(break_label, ref opt_expr) => { if let Some(e) = opt_expr { self.visit_expr(e); } - if self.require_label_in_labeled_block(e.span, &label, "break") { + if self.require_label_in_labeled_block(e.span, &break_label, "break") { // If we emitted an error about an unlabeled break in a labeled // block, we don't need any further checking for this break any more return; } - let loop_id = match label.target_id { + let loop_id = match break_label.target_id { Ok(loop_id) => Some(loop_id), Err(hir::LoopIdError::OutsideLoopScope) => None, Err(hir::LoopIdError::UnlabeledCfInWhileCondition) => { @@ -89,49 +89,89 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { Err(hir::LoopIdError::UnresolvedLabel) => None, }; - if let Some(loop_id) = loop_id { - if let Node::Block(_) = self.hir_map.find(loop_id).unwrap() { - return; - } + if let Some(Node::Block(_)) = loop_id.and_then(|id| self.hir_map.find(id)) { + return; } - if opt_expr.is_some() { - let loop_kind = if let Some(loop_id) = loop_id { - Some(match self.hir_map.expect_expr(loop_id).kind { - hir::ExprKind::Loop(_, _, source) => source, + if let Some(break_expr) = opt_expr { + let (head, loop_label, loop_kind) = if let Some(loop_id) = loop_id { + match self.hir_map.expect_expr(loop_id).kind { + hir::ExprKind::Loop(_, label, source, sp) => { + (Some(sp), label, Some(source)) + } ref r => { span_bug!(e.span, "break label resolved to a non-loop: {:?}", r) } - }) + } } else { - None + (None, None, None) }; match loop_kind { None | Some(hir::LoopSource::Loop) => (), Some(kind) => { - struct_span_err!( + let mut err = struct_span_err!( self.sess, e.span, E0571, "`break` with value from a `{}` loop", kind.name() - ) - .span_label( + ); + err.span_label( e.span, - "can only break with a value inside \ - `loop` or breakable block", - ) - .span_suggestion( + "can only break with a value inside `loop` or breakable block", + ); + if let Some(head) = head { + err.span_label( + head, + &format!( + "you can't `break` with a value in a `{}` loop", + kind.name() + ), + ); + } + err.span_suggestion( e.span, &format!( - "instead, use `break` on its own \ - without a value inside this `{}` loop", - kind.name() + "use `break` on its own without a value inside this `{}` loop", + kind.name(), + ), + format!( + "break{}", + break_label + .label + .map_or_else(String::new, |l| format!(" {}", l.ident)) ), - "break".to_string(), Applicability::MaybeIncorrect, - ) - .emit(); + ); + if let (Some(label), None) = (loop_label, break_label.label) { + match break_expr.kind { + hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { + segments: [segment], + res: hir::def::Res::Err, + .. + }, + )) if label.ident.to_string() + == format!("'{}", segment.ident) => + { + // This error is redundant, we will have already emitted a + // suggestion to use the label when `segment` wasn't found + // (hence the `Res::Err` check). + err.delay_as_bug(); + } + _ => { + err.span_suggestion( + break_expr.span, + "alternatively, you might have meant to use the \ + available loop label", + label.ident.to_string(), + Applicability::MaybeIncorrect, + ); + } + } + } + err.emit(); } } } diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 5b50ef8627..93fb23c018 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -149,7 +149,7 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, hir_id: HirId, body: &'tcx hir::Body<'tcx>, fn_span: Span) { let mut this = CheckInlineAssembly { tcx, items: Vec::new() }; this.visit_body(body); - if let &[(ItemKind::Asm, _)] = &this.items[..] { + if let [(ItemKind::Asm, _)] = this.items[..] { // Ok. } else { tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, fn_span, |lint| { @@ -201,6 +201,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> { | ExprKind::Type(..) | ExprKind::Loop(..) | ExprKind::Match(..) + | ExprKind::If(..) | ExprKind::Closure(..) | ExprKind::Assign(..) | ExprKind::AssignOp(..) diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index fde83af99a..eb24c51c54 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -307,6 +307,7 @@ impl<'tcx> ReachableContext<'tcx> { | Node::Ctor(..) | Node::Field(_) | Node::Ty(_) + | Node::Crate(_) | Node::MacroDef(_) => {} _ => { bug!( @@ -349,7 +350,9 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx } // We need only trait impls here, not inherent impls, and only non-exported ones - if let hir::ItemKind::Impl { of_trait: Some(ref trait_ref), ref items, .. } = item.kind { + if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), ref items, .. }) = + item.kind + { if !self.access_levels.is_reachable(item.hir_id) { // FIXME(#53488) remove `let` let tcx = self.tcx; diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_passes/src/region.rs index 1af79abe4b..64356f73f6 100644 --- a/compiler/rustc_passes/src/region.rs +++ b/compiler/rustc_passes/src/region.rs @@ -241,7 +241,18 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h terminating(r.hir_id.local_id); } - hir::ExprKind::Loop(ref body, _, _) => { + hir::ExprKind::If(ref expr, ref then, Some(ref otherwise)) => { + terminating(expr.hir_id.local_id); + terminating(then.hir_id.local_id); + terminating(otherwise.hir_id.local_id); + } + + hir::ExprKind::If(ref expr, ref then, None) => { + terminating(expr.hir_id.local_id); + terminating(then.hir_id.local_id); + } + + hir::ExprKind::Loop(ref body, _, _, _) => { terminating(body.hir_id.local_id); } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 3c2462aab2..e1d03e3504 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -55,6 +55,21 @@ impl InheritDeprecation { } } +/// Whether to inherit const stability flags for nested items. In most cases, we do not want to +/// inherit const stability: just because an enclosing `fn` is const-stable does not mean +/// all `extern` imports declared in it should be const-stable! However, trait methods +/// inherit const stability attributes from their parent and do not have their own. +enum InheritConstStability { + Yes, + No, +} + +impl InheritConstStability { + fn yes(&self) -> bool { + matches!(self, InheritConstStability::Yes) + } +} + // A private tree-walker for producing an Index. struct Annotator<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -75,6 +90,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { item_sp: Span, kind: AnnotationKind, inherit_deprecation: InheritDeprecation, + inherit_const_stability: InheritConstStability, visit_children: F, ) where F: FnOnce(&mut Self), @@ -140,6 +156,8 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { const_stab }); + // `impl const Trait for Type` items forward their const stability to their + // immediate children. if const_stab.is_none() { debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); if let Some(parent) = self.parent_const_stab { @@ -228,7 +246,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { self.recurse_with_stability_attrs( depr.map(|(d, _)| DeprecationEntry::local(d, hir_id)), stab, - const_stab, + if inherit_const_stability.yes() { const_stab } else { None }, visit_children, ); } @@ -325,18 +343,21 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_item(&mut self, i: &'tcx Item<'tcx>) { let orig_in_trait_impl = self.in_trait_impl; let mut kind = AnnotationKind::Required; + let mut const_stab_inherit = InheritConstStability::No; match i.kind { // Inherent impls and foreign modules serve only as containers for other items, // they don't have their own stability. They still can be annotated as unstable // and propagate this unstability to children, but this annotation is completely // optional. They inherit stability from their parents when unannotated. - hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod { .. } => { + hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) + | hir::ItemKind::ForeignMod { .. } => { self.in_trait_impl = false; kind = AnnotationKind::Container; } - hir::ItemKind::Impl { of_trait: Some(_), .. } => { + hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => { self.in_trait_impl = true; kind = AnnotationKind::DeprecationProhibited; + const_stab_inherit = InheritConstStability::Yes; } hir::ItemKind::Struct(ref sd, _) => { if let Some(ctor_hir_id) = sd.ctor_hir_id() { @@ -346,6 +367,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { i.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |_| {}, ) } @@ -353,9 +375,15 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { _ => {} } - self.annotate(i.hir_id, &i.attrs, i.span, kind, InheritDeprecation::Yes, |v| { - intravisit::walk_item(v, i) - }); + self.annotate( + i.hir_id, + &i.attrs, + i.span, + kind, + InheritDeprecation::Yes, + const_stab_inherit, + |v| intravisit::walk_item(v, i), + ); self.in_trait_impl = orig_in_trait_impl; } @@ -366,6 +394,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { ti.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |v| { intravisit::walk_trait_item(v, ti); }, @@ -375,9 +404,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { let kind = if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required }; - self.annotate(ii.hir_id, &ii.attrs, ii.span, kind, InheritDeprecation::Yes, |v| { - intravisit::walk_impl_item(v, ii); - }); + self.annotate( + ii.hir_id, + &ii.attrs, + ii.span, + kind, + InheritDeprecation::Yes, + InheritConstStability::No, + |v| { + intravisit::walk_impl_item(v, ii); + }, + ); } fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) { @@ -387,6 +424,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { var.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |v| { if let Some(ctor_hir_id) = var.data.ctor_hir_id() { v.annotate( @@ -395,6 +433,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { var.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |_| {}, ); } @@ -411,6 +450,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { s.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |v| { intravisit::walk_struct_field(v, s); }, @@ -424,6 +464,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { i.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |v| { intravisit::walk_foreign_item(v, i); }, @@ -437,22 +478,31 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { md.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |_| {}, ); } fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { let kind = match &p.kind { - // FIXME(const_generics:defaults) + // FIXME(const_generics_defaults) hir::GenericParamKind::Type { default, .. } if default.is_some() => { AnnotationKind::Container } _ => AnnotationKind::Prohibited, }; - self.annotate(p.hir_id, &p.attrs, p.span, kind, InheritDeprecation::No, |v| { - intravisit::walk_generic_param(v, p); - }); + self.annotate( + p.hir_id, + &p.attrs, + p.span, + kind, + InheritDeprecation::No, + InheritConstStability::No, + |v| { + intravisit::walk_generic_param(v, p); + }, + ); } } @@ -503,7 +553,8 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { // optional. They inherit stability from their parents when unannotated. if !matches!( i.kind, - hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod { .. } + hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) + | hir::ItemKind::ForeignMod { .. } ) { self.check_missing_stability(i.hir_id, i.span); } @@ -617,6 +668,7 @@ fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> { krate.item.span, AnnotationKind::Required, InheritDeprecation::Yes, + InheritConstStability::No, |v| intravisit::walk_crate(v, krate), ); } @@ -672,7 +724,7 @@ impl Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl { of_trait: Some(ref t), self_ty, items, .. } => { + hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => { if self.tcx.features().staged_api { // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 4273d60000..daff94cb6d 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -59,7 +59,7 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { } } - for (name, &item) in WEAK_ITEMS_REFS.iter() { + for (name, item) in WEAK_ITEMS_REFS.clone().into_sorted_vector().into_iter() { if missing.contains(&item) && required(tcx, item) && items.require(item).is_err() { if item == LangItem::PanicImpl { tcx.sess.err("`#[panic_handler]` function required, but not found"); diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml index ce83dc1de7..85e584d543 100644 --- a/compiler/rustc_privacy/Cargo.toml +++ b/compiler/rustc_privacy/Cargo.toml @@ -13,4 +13,5 @@ rustc_typeck = { path = "../rustc_typeck" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_trait_selection = { path = "../rustc_trait_selection" } tracing = "0.1" diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 3b4249a93e..631dcb6059 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -18,15 +18,17 @@ use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind}; use rustc_middle::bug; use rustc_middle::hir::map::Map; use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; +use rustc_middle::mir::abstract_const::Node as ACNode; use rustc_middle::span_bug; use rustc_middle::ty::fold::TypeVisitor; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::{self, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::{self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable}; use rustc_session::lint; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; +use rustc_trait_selection::traits::const_evaluatable::{self, AbstractConst}; use std::marker::PhantomData; use std::ops::ControlFlow; @@ -100,31 +102,47 @@ where } fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow { - match predicate.skip_binders() { - ty::PredicateAtom::Trait(ty::TraitPredicate { trait_ref }, _) => { + match predicate.kind().skip_binder() { + ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref }, _) => { self.visit_trait(trait_ref) } - ty::PredicateAtom::Projection(ty::ProjectionPredicate { projection_ty, ty }) => { + ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, ty }) => { ty.visit_with(self)?; self.visit_trait(projection_ty.trait_ref(self.def_id_visitor.tcx())) } - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, _region)) => { + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, _region)) => { ty.visit_with(self) } - ty::PredicateAtom::RegionOutlives(..) => ControlFlow::CONTINUE, - ty::PredicateAtom::ConstEvaluatable(..) + ty::PredicateKind::RegionOutlives(..) => ControlFlow::CONTINUE, + ty::PredicateKind::ConstEvaluatable(defs, substs) if self.def_id_visitor.tcx().features().const_evaluatable_checked => { - // FIXME(const_evaluatable_checked): If the constant used here depends on a - // private function we may have to do something here... - // - // For now, let's just pretend that everything is fine. + let tcx = self.def_id_visitor.tcx(); + if let Ok(Some(ct)) = AbstractConst::new(tcx, defs, substs) { + self.visit_abstract_const_expr(tcx, ct)?; + } ControlFlow::CONTINUE } _ => bug!("unexpected predicate: {:?}", predicate), } } + fn visit_abstract_const_expr( + &mut self, + tcx: TyCtxt<'tcx>, + ct: AbstractConst<'tcx>, + ) -> ControlFlow { + const_evaluatable::walk_abstract_const(tcx, ct, |node| match node.root() { + ACNode::Leaf(leaf) => { + let leaf = leaf.subst(tcx, ct.substs); + self.visit_const(leaf) + } + ACNode::Binop(..) | ACNode::UnaryOp(..) | ACNode::FunctionCall(_, _) => { + ControlFlow::CONTINUE + } + }) + } + fn visit_predicates( &mut self, predicates: ty::GenericPredicates<'tcx>, @@ -241,6 +259,15 @@ where ty.super_visit_with(self) } } + + fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow { + self.visit_ty(c.ty)?; + let tcx = self.def_id_visitor.tcx(); + if let Ok(Some(ct)) = AbstractConst::from_const(tcx, c) { + self.visit_abstract_const_expr(tcx, ct)?; + } + ControlFlow::CONTINUE + } } fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visibility { @@ -632,9 +659,9 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { } } } - hir::ItemKind::Impl { ref of_trait, items, .. } => { - for impl_item_ref in items { - if of_trait.is_some() || impl_item_ref.vis.node.is_pub() { + hir::ItemKind::Impl(ref impl_) => { + for impl_item_ref in impl_.items { + if impl_.of_trait.is_some() || impl_item_ref.vis.node.is_pub() { self.update(impl_item_ref.id.hir_id, item_level); } } @@ -736,11 +763,11 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { } } // Visit everything except for private impl items. - hir::ItemKind::Impl { items, .. } => { + hir::ItemKind::Impl(ref impl_) => { if item_level.is_some() { self.reach(item.hir_id, item_level).generics().predicates().ty().trait_ref(); - for impl_item_ref in items { + for impl_item_ref in impl_.items { let impl_item_level = self.get(impl_item_ref.id.hir_id); if impl_item_level.is_some() { self.reach(impl_item_ref.id.hir_id, impl_item_level) @@ -832,21 +859,24 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { } fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) { + // Non-opaque macros cannot make other items more accessible than they already are. if attr::find_transparency(&self.tcx.sess, &md.attrs, md.ast.macro_rules).0 != Transparency::Opaque { - self.update(md.hir_id, Some(AccessLevel::Public)); + // `#[macro_export]`-ed `macro_rules!` are `Public` since they + // ignore their containing path to always appear at the crate root. + if md.ast.macro_rules { + self.update(md.hir_id, Some(AccessLevel::Public)); + } return; } let macro_module_def_id = ty::DefIdTree::parent(self.tcx, self.tcx.hir().local_def_id(md.hir_id).to_def_id()) .unwrap(); - // FIXME(#71104) Should really be using just `as_local_hir_id` but - // some `DefId` do not seem to have a corresponding HirId. let hir_id = macro_module_def_id .as_local() - .and_then(|def_id| self.tcx.hir().opt_local_def_id_to_hir_id(def_id)); + .map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)); let mut module_id = match hir_id { Some(module_id) if self.tcx.hir().is_hir_id_module(module_id) => module_id, // `module_id` doesn't correspond to a `mod`, return early (#63164, #65252). @@ -959,7 +989,7 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { in_update_syntax: bool, ) { // definition of the field - let ident = Ident::new(kw::Invalid, use_ctxt); + let ident = Ident::new(kw::Empty, use_ctxt); let current_hir = self.current_item.unwrap(); let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did, current_hir).1; if !def.is_enum() && !field.vis.is_accessible_from(def_id, self.tcx) { @@ -1447,7 +1477,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { // (i.e., we could just return here to not check them at // all, or some worse estimation of whether an impl is // publicly visible). - hir::ItemKind::Impl { generics: ref g, ref of_trait, ref self_ty, items, .. } => { + hir::ItemKind::Impl(ref impl_) => { // `impl [... for] Private` is never visible. let self_contains_private; // `impl [... for] Public<...>`, but not `impl [... for] @@ -1462,7 +1492,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { at_outer_type: true, outer_type_is_public_path: false, }; - visitor.visit_ty(&self_ty); + visitor.visit_ty(&impl_.self_ty); self_contains_private = visitor.contains_private; self_is_public_path = visitor.outer_type_is_public_path; } @@ -1470,7 +1500,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { // Miscellaneous info about the impl: // `true` iff this is `impl Private for ...`. - let not_private_trait = of_trait.as_ref().map_or( + let not_private_trait = impl_.of_trait.as_ref().map_or( true, // no trait counts as public trait |tr| { let did = tr.path.res.def_id(); @@ -1491,8 +1521,8 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { // directly because we might have `impl> ...`, // and we shouldn't warn about the generics if all the methods // are private (because `T` won't be visible externally). - let trait_or_some_public_method = of_trait.is_some() - || items.iter().any(|impl_item_ref| { + let trait_or_some_public_method = impl_.of_trait.is_some() + || impl_.items.iter().any(|impl_item_ref| { let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); match impl_item.kind { hir::ImplItemKind::Const(..) | hir::ImplItemKind::Fn(..) => { @@ -1503,11 +1533,11 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { }); if !self_contains_private && not_private_trait && trait_or_some_public_method { - intravisit::walk_generics(self, g); + intravisit::walk_generics(self, &impl_.generics); - match of_trait { + match impl_.of_trait { None => { - for impl_item_ref in items { + for impl_item_ref in impl_.items { // This is where we choose whether to walk down // further into the impl to check its items. We // should only walk into public items so that we @@ -1528,7 +1558,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { } } } - Some(tr) => { + Some(ref tr) => { // Any private types in a trait impl fall into three // categories. // 1. mentioned in the trait definition @@ -1545,7 +1575,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { intravisit::walk_path(self, &tr.path); // Those in 3. are warned with this call. - for impl_item_ref in items { + for impl_item_ref in impl_.items { let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); if let hir::ImplItemKind::TyAlias(ref ty) = impl_item.kind { self.visit_ty(ty); @@ -1553,11 +1583,11 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { } } } - } else if of_trait.is_none() && self_is_public_path { + } else if impl_.of_trait.is_none() && self_is_public_path { // `impl Public { ... }`. Any public static // methods will be visible as `Public::foo`. let mut found_pub_static = false; - for impl_item_ref in items { + for impl_item_ref in impl_.items { if self.item_is_public(&impl_item_ref.id.hir_id, &impl_item_ref.vis) { let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); match impl_item_ref.kind { @@ -1574,7 +1604,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { } } if found_pub_static { - intravisit::walk_generics(self, g) + intravisit::walk_generics(self, &impl_.generics) } } return; @@ -1967,11 +1997,11 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> // Subitems of inherent impls have their own publicity. // A trait impl is public when both its type and its trait are public // Subitems of trait impls have inherited publicity. - hir::ItemKind::Impl { ref of_trait, items, .. } => { + hir::ItemKind::Impl(ref impl_) => { let impl_vis = ty::Visibility::of_impl(item.hir_id, tcx, &Default::default()); self.check(item.hir_id, impl_vis).generics().predicates(); - for impl_item_ref in items { - let impl_item_vis = if of_trait.is_none() { + for impl_item_ref in impl_.items { + let impl_item_vis = if impl_.of_trait.is_none() { min( tcx.visibility(tcx.hir().local_def_id(impl_item_ref.id.hir_id)), impl_vis, @@ -2029,7 +2059,7 @@ fn visibility(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Visibility { Node::ImplItem(impl_item) => { match tcx.hir().get(tcx.hir().get_parent_item(hir_id)) { Node::Item(hir::Item { - kind: hir::ItemKind::Impl { of_trait: Some(tr), .. }, + kind: hir::ItemKind::Impl(hir::Impl { of_trait: Some(tr), .. }), .. }) => tr.path.res.opt_def_id().map_or_else( || { diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index ff52fdab19..64aba87050 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -153,12 +153,6 @@ where } } -impl DepNodeParams for () { - fn to_fingerprint(&self, _: Ctxt) -> Fingerprint { - Fingerprint::ZERO - } -} - /// A "work product" corresponds to a `.o` (or other) file that we /// save in between runs. These IDs do not have a `DefId` but rather /// some independent path or string that persists between runs without diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 605d7ae4af..4fb3a683ea 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -7,6 +7,7 @@ use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, LockGuard, Lrc, Or use rustc_data_structures::unlikely; use rustc_errors::Diagnostic; use rustc_index::vec::{Idx, IndexVec}; +use rustc_serialize::{Encodable, Encoder}; use parking_lot::{Condvar, Mutex}; use smallvec::{smallvec, SmallVec}; @@ -21,7 +22,7 @@ use std::sync::atomic::Ordering::Relaxed; use super::debug::EdgeFilter; use super::prev::PreviousDepGraph; use super::query::DepGraphQuery; -use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; +use super::serialized::SerializedDepNodeIndex; use super::{DepContext, DepKind, DepNode, WorkProductId}; #[derive(Clone)] @@ -148,7 +149,7 @@ impl DepGraph { let mut edge_list_indices = Vec::with_capacity(node_count); let mut edge_list_data = Vec::with_capacity(edge_count); - // See `serialize` for notes on the approach used here. + // See `DepGraph`'s `Encodable` implementation for notes on the approach used here. edge_list_data.extend(data.unshared_edges.iter().map(|i| i.index())); @@ -551,19 +552,6 @@ impl DepGraph { self.data.as_ref()?.dep_node_debug.borrow().get(&dep_node).cloned() } - pub fn edge_deduplication_data(&self) -> Option<(u64, u64)> { - if cfg!(debug_assertions) { - let current_dep_graph = &self.data.as_ref().unwrap().current; - - Some(( - current_dep_graph.total_read_count.load(Relaxed), - current_dep_graph.total_duplicate_read_count.load(Relaxed), - )) - } else { - None - } - } - fn edge_count(&self, node_data: &LockGuard<'_, DepNodeData>) -> usize { let data = self.data.as_ref().unwrap(); let previous = &data.previous; @@ -579,84 +567,6 @@ impl DepGraph { edge_count } - pub fn serialize(&self) -> SerializedDepGraph { - type SDNI = SerializedDepNodeIndex; - - let data = self.data.as_ref().unwrap(); - let previous = &data.previous; - - // Note locking order: `prev_index_to_index`, then `data`. - let prev_index_to_index = data.current.prev_index_to_index.lock(); - let data = data.current.data.lock(); - let node_count = data.hybrid_indices.len(); - let edge_count = self.edge_count(&data); - - let mut nodes = IndexVec::with_capacity(node_count); - let mut fingerprints = IndexVec::with_capacity(node_count); - let mut edge_list_indices = IndexVec::with_capacity(node_count); - let mut edge_list_data = Vec::with_capacity(edge_count); - - // `rustc_middle::ty::query::OnDiskCache` expects nodes to be in - // `DepNodeIndex` order. The edges in `edge_list_data`, on the other - // hand, don't need to be in a particular order, as long as each node - // can reference its edges as a contiguous range within it. This is why - // we're able to copy `unshared_edges` directly into `edge_list_data`. - // It meets the above requirements, and each non-dark-green node already - // knows the range of edges to reference within it, which they'll push - // onto `edge_list_indices`. Dark green nodes, however, don't have their - // edges in `unshared_edges`, so need to add them to `edge_list_data`. - - edge_list_data.extend(data.unshared_edges.iter().map(|i| SDNI::new(i.index()))); - - for &hybrid_index in data.hybrid_indices.iter() { - match hybrid_index.into() { - HybridIndex::New(i) => { - let new = &data.new; - nodes.push(new.nodes[i]); - fingerprints.push(new.fingerprints[i]); - let edges = &new.edges[i]; - edge_list_indices.push((edges.start.as_u32(), edges.end.as_u32())); - } - HybridIndex::Red(i) => { - let red = &data.red; - nodes.push(previous.index_to_node(red.node_indices[i])); - fingerprints.push(red.fingerprints[i]); - let edges = &red.edges[i]; - edge_list_indices.push((edges.start.as_u32(), edges.end.as_u32())); - } - HybridIndex::LightGreen(i) => { - let lg = &data.light_green; - nodes.push(previous.index_to_node(lg.node_indices[i])); - fingerprints.push(previous.fingerprint_by_index(lg.node_indices[i])); - let edges = &lg.edges[i]; - edge_list_indices.push((edges.start.as_u32(), edges.end.as_u32())); - } - HybridIndex::DarkGreen(prev_index) => { - nodes.push(previous.index_to_node(prev_index)); - fingerprints.push(previous.fingerprint_by_index(prev_index)); - - let edges_iter = previous - .edge_targets_from(prev_index) - .iter() - .map(|&dst| prev_index_to_index[dst].as_ref().unwrap()); - - let start = edge_list_data.len() as u32; - edge_list_data.extend(edges_iter.map(|i| SDNI::new(i.index()))); - let end = edge_list_data.len() as u32; - edge_list_indices.push((start, end)); - } - } - } - - debug_assert_eq!(nodes.len(), node_count); - debug_assert_eq!(fingerprints.len(), node_count); - debug_assert_eq!(edge_list_indices.len(), node_count); - debug_assert_eq!(edge_list_data.len(), edge_count); - debug_assert!(edge_list_data.len() <= u32::MAX as usize); - - SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data } - } - pub fn node_color(&self, dep_node: &DepNode) -> Option { if let Some(ref data) = self.data { if let Some(prev_index) = data.previous.node_to_index_opt(dep_node) { @@ -953,7 +863,7 @@ impl DepGraph { // Returns true if the given node has been marked as green during the // current compilation session. Used in various assertions pub fn is_green(&self, dep_node: &DepNode) -> bool { - self.node_color(dep_node).map(|c| c.is_green()).unwrap_or(false) + self.node_color(dep_node).map_or(false, |c| c.is_green()) } // This method loads all on-disk cacheable query results into memory, so @@ -997,12 +907,251 @@ impl DepGraph { } } + pub fn print_incremental_info(&self) { + #[derive(Clone)] + struct Stat { + kind: Kind, + node_counter: u64, + edge_counter: u64, + } + + let data = self.data.as_ref().unwrap(); + let prev = &data.previous; + let current = &data.current; + let data = current.data.lock(); + + let mut stats: FxHashMap<_, Stat> = FxHashMap::with_hasher(Default::default()); + + for &hybrid_index in data.hybrid_indices.iter() { + let (kind, edge_count) = match hybrid_index.into() { + HybridIndex::New(new_index) => { + let kind = data.new.nodes[new_index].kind; + let edge_range = &data.new.edges[new_index]; + (kind, edge_range.end.as_usize() - edge_range.start.as_usize()) + } + HybridIndex::Red(red_index) => { + let kind = prev.index_to_node(data.red.node_indices[red_index]).kind; + let edge_range = &data.red.edges[red_index]; + (kind, edge_range.end.as_usize() - edge_range.start.as_usize()) + } + HybridIndex::LightGreen(lg_index) => { + let kind = prev.index_to_node(data.light_green.node_indices[lg_index]).kind; + let edge_range = &data.light_green.edges[lg_index]; + (kind, edge_range.end.as_usize() - edge_range.start.as_usize()) + } + HybridIndex::DarkGreen(prev_index) => { + let kind = prev.index_to_node(prev_index).kind; + let edge_count = prev.edge_targets_from(prev_index).len(); + (kind, edge_count) + } + }; + + let stat = stats.entry(kind).or_insert(Stat { kind, node_counter: 0, edge_counter: 0 }); + stat.node_counter += 1; + stat.edge_counter += edge_count as u64; + } + + let total_node_count = data.hybrid_indices.len(); + let total_edge_count = self.edge_count(&data); + + // Drop the lock guard. + std::mem::drop(data); + + let mut stats: Vec<_> = stats.values().cloned().collect(); + stats.sort_by_key(|s| -(s.node_counter as i64)); + + const SEPARATOR: &str = "[incremental] --------------------------------\ + ----------------------------------------------\ + ------------"; + + println!("[incremental]"); + println!("[incremental] DepGraph Statistics"); + println!("{}", SEPARATOR); + println!("[incremental]"); + println!("[incremental] Total Node Count: {}", total_node_count); + println!("[incremental] Total Edge Count: {}", total_edge_count); + + if cfg!(debug_assertions) { + let total_edge_reads = current.total_read_count.load(Relaxed); + let total_duplicate_edge_reads = current.total_duplicate_read_count.load(Relaxed); + + println!("[incremental] Total Edge Reads: {}", total_edge_reads); + println!("[incremental] Total Duplicate Edge Reads: {}", total_duplicate_edge_reads); + } + + println!("[incremental]"); + + println!( + "[incremental] {:<36}| {:<17}| {:<12}| {:<17}|", + "Node Kind", "Node Frequency", "Node Count", "Avg. Edge Count" + ); + + println!( + "[incremental] -------------------------------------\ + |------------------\ + |-------------\ + |------------------|" + ); + + for stat in stats { + let node_kind_ratio = (100.0 * (stat.node_counter as f64)) / (total_node_count as f64); + let node_kind_avg_edges = (stat.edge_counter as f64) / (stat.node_counter as f64); + + println!( + "[incremental] {:<36}|{:>16.1}% |{:>12} |{:>17.1} |", + format!("{:?}", stat.kind), + node_kind_ratio, + stat.node_counter, + node_kind_avg_edges, + ); + } + + println!("{}", SEPARATOR); + println!("[incremental]"); + } + fn next_virtual_depnode_index(&self) -> DepNodeIndex { let index = self.virtual_dep_node_index.fetch_add(1, Relaxed); DepNodeIndex::from_u32(index) } } +impl> Encodable for DepGraph { + fn encode(&self, e: &mut E) -> Result<(), E::Error> { + // We used to serialize the dep graph by creating and serializing a `SerializedDepGraph` + // using data copied from the `DepGraph`. But copying created a large memory spike, so we + // now serialize directly from the `DepGraph` as if it's a `SerializedDepGraph`. Because we + // deserialize that data into a `SerializedDepGraph` in the next compilation session, we + // need `DepGraph`'s `Encodable` and `SerializedDepGraph`'s `Decodable` implementations to + // be in sync. If you update this encoding, be sure to update the decoding, and vice-versa. + + let data = self.data.as_ref().unwrap(); + let prev = &data.previous; + + // Note locking order: `prev_index_to_index`, then `data`. + let prev_index_to_index = data.current.prev_index_to_index.lock(); + let data = data.current.data.lock(); + let new = &data.new; + let red = &data.red; + let lg = &data.light_green; + + let node_count = data.hybrid_indices.len(); + let edge_count = self.edge_count(&data); + + // `rustc_middle::ty::query::OnDiskCache` expects nodes to be encoded in `DepNodeIndex` + // order. The edges in `edge_list_data` don't need to be in a particular order, as long as + // each node references its edges as a contiguous range within it. Therefore, we can encode + // `edge_list_data` directly from `unshared_edges`. It meets the above requirements, as + // each non-dark-green node already knows the range of edges to reference within it, which + // they'll encode in `edge_list_indices`. Dark green nodes, however, don't have their edges + // in `unshared_edges`, so need to add them to `edge_list_data`. + + use HybridIndex::*; + + // Encoded values (nodes, etc.) are explicitly typed below to avoid inadvertently + // serializing data in the wrong format (i.e. one incompatible with `SerializedDepGraph`). + e.emit_struct("SerializedDepGraph", 4, |e| { + e.emit_struct_field("nodes", 0, |e| { + // `SerializedDepGraph` expects this to be encoded as a sequence of `DepNode`s. + e.emit_seq(node_count, |e| { + for (seq_index, &hybrid_index) in data.hybrid_indices.iter().enumerate() { + let node: DepNode = match hybrid_index.into() { + New(i) => new.nodes[i], + Red(i) => prev.index_to_node(red.node_indices[i]), + LightGreen(i) => prev.index_to_node(lg.node_indices[i]), + DarkGreen(prev_index) => prev.index_to_node(prev_index), + }; + + e.emit_seq_elt(seq_index, |e| node.encode(e))?; + } + + Ok(()) + }) + })?; + + e.emit_struct_field("fingerprints", 1, |e| { + // `SerializedDepGraph` expects this to be encoded as a sequence of `Fingerprints`s. + e.emit_seq(node_count, |e| { + for (seq_index, &hybrid_index) in data.hybrid_indices.iter().enumerate() { + let fingerprint: Fingerprint = match hybrid_index.into() { + New(i) => new.fingerprints[i], + Red(i) => red.fingerprints[i], + LightGreen(i) => prev.fingerprint_by_index(lg.node_indices[i]), + DarkGreen(prev_index) => prev.fingerprint_by_index(prev_index), + }; + + e.emit_seq_elt(seq_index, |e| fingerprint.encode(e))?; + } + + Ok(()) + }) + })?; + + e.emit_struct_field("edge_list_indices", 2, |e| { + // `SerializedDepGraph` expects this to be encoded as a sequence of `(u32, u32)`s. + e.emit_seq(node_count, |e| { + // Dark green node edges start after the unshared (all other nodes') edges. + let mut dark_green_edge_index = data.unshared_edges.len(); + + for (seq_index, &hybrid_index) in data.hybrid_indices.iter().enumerate() { + let edge_indices: (u32, u32) = match hybrid_index.into() { + New(i) => (new.edges[i].start.as_u32(), new.edges[i].end.as_u32()), + Red(i) => (red.edges[i].start.as_u32(), red.edges[i].end.as_u32()), + LightGreen(i) => (lg.edges[i].start.as_u32(), lg.edges[i].end.as_u32()), + DarkGreen(prev_index) => { + let edge_count = prev.edge_targets_from(prev_index).len(); + let start = dark_green_edge_index as u32; + dark_green_edge_index += edge_count; + let end = dark_green_edge_index as u32; + (start, end) + } + }; + + e.emit_seq_elt(seq_index, |e| edge_indices.encode(e))?; + } + + assert_eq!(dark_green_edge_index, edge_count); + + Ok(()) + }) + })?; + + e.emit_struct_field("edge_list_data", 3, |e| { + // `SerializedDepGraph` expects this to be encoded as a sequence of + // `SerializedDepNodeIndex`. + e.emit_seq(edge_count, |e| { + for (seq_index, &edge) in data.unshared_edges.iter().enumerate() { + let serialized_edge = SerializedDepNodeIndex::new(edge.index()); + e.emit_seq_elt(seq_index, |e| serialized_edge.encode(e))?; + } + + let mut seq_index = data.unshared_edges.len(); + + for &hybrid_index in data.hybrid_indices.iter() { + if let DarkGreen(prev_index) = hybrid_index.into() { + for &edge in prev.edge_targets_from(prev_index) { + // Dark green node edges are stored in the previous graph + // and must be converted to edges in the current graph, + // and then serialized as `SerializedDepNodeIndex`. + let serialized_edge = SerializedDepNodeIndex::new( + prev_index_to_index[edge].as_ref().unwrap().index(), + ); + + e.emit_seq_elt(seq_index, |e| serialized_edge.encode(e))?; + seq_index += 1; + } + } + } + + assert_eq!(seq_index, edge_count); + + Ok(()) + }) + }) + }) + } +} + /// A "work product" is an intermediate result that we save into the /// incremental directory for later re-use. The primary example are /// the object files that we save for each partition at code diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index da0b5aad6c..b1c901633a 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -61,7 +61,7 @@ pub trait DepContext: Copy { } /// Describe the different families of dependency nodes. -pub trait DepKind: Copy + fmt::Debug + Eq + Ord + Hash { +pub trait DepKind: Copy + fmt::Debug + Eq + Hash { const NULL: Self; /// Return whether this kind always require evaluation. diff --git a/compiler/rustc_query_system/src/dep_graph/prev.rs b/compiler/rustc_query_system/src/dep_graph/prev.rs index 29357ce944..c3d0f79525 100644 --- a/compiler/rustc_query_system/src/dep_graph/prev.rs +++ b/compiler/rustc_query_system/src/dep_graph/prev.rs @@ -3,7 +3,7 @@ use super::{DepKind, DepNode}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; -#[derive(Debug, Encodable, Decodable)] +#[derive(Debug)] pub struct PreviousDepGraph { data: SerializedDepGraph, index: FxHashMap, SerializedDepNodeIndex>, diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 28e0740691..9bb922b0a9 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -3,6 +3,7 @@ use super::{DepKind, DepNode}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_index::vec::IndexVec; +use rustc_serialize::{Decodable, Decoder}; // The maximum value of `SerializedDepNodeIndex` leaves the upper two bits // unused so that we can store multiple index types in `CompressedHybridIndex`, @@ -14,7 +15,7 @@ rustc_index::newtype_index! { } /// Data for use when recompiling the **current crate**. -#[derive(Debug, Encodable, Decodable)] +#[derive(Debug)] pub struct SerializedDepGraph { /// The set of all DepNodes in the graph pub nodes: IndexVec>, @@ -48,3 +49,79 @@ impl SerializedDepGraph { &self.edge_list_data[targets.0 as usize..targets.1 as usize] } } + +impl> Decodable for SerializedDepGraph { + fn decode(d: &mut D) -> Result, D::Error> { + // We used to serialize the dep graph by creating and serializing a `SerializedDepGraph` + // using data copied from the `DepGraph`. But copying created a large memory spike, so we + // now serialize directly from the `DepGraph` as if it's a `SerializedDepGraph`. Because we + // deserialize that data into a `SerializedDepGraph` in the next compilation session, we + // need `DepGraph`'s `Encodable` and `SerializedDepGraph`'s `Decodable` implementations to + // be in sync. If you update this decoding, be sure to update the encoding, and vice-versa. + // + // We mimic the sequence of `Encode` and `Encodable` method calls used by the `DepGraph`'s + // `Encodable` implementation with the corresponding sequence of `Decode` and `Decodable` + // method calls. E.g. `Decode::read_struct` pairs with `Encode::emit_struct`, `DepNode`'s + // `decode` pairs with `DepNode`'s `encode`, and so on. Any decoding methods not associated + // with corresponding encoding methods called in `DepGraph`'s `Encodable` implementation + // are off limits, because we'd be relying on their implementation details. + // + // For example, because we know it happens to do the right thing, its tempting to just use + // `IndexVec`'s `Decodable` implementation to decode into some of the collections below, + // even though `DepGraph` doesn't use its `Encodable` implementation. But the `IndexVec` + // implementation could change, and we'd have a bug. + // + // Variables below are explicitly typed so that anyone who changes the `SerializedDepGraph` + // representation without updating this function will encounter a compilation error, and + // know to update this and possibly the `DepGraph` `Encodable` implementation accordingly + // (the latter should serialize data in a format compatible with our representation). + + d.read_struct("SerializedDepGraph", 4, |d| { + let nodes: IndexVec> = + d.read_struct_field("nodes", 0, |d| { + d.read_seq(|d, len| { + let mut v = IndexVec::with_capacity(len); + for i in 0..len { + v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(v) + }) + })?; + + let fingerprints: IndexVec = + d.read_struct_field("fingerprints", 1, |d| { + d.read_seq(|d, len| { + let mut v = IndexVec::with_capacity(len); + for i in 0..len { + v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(v) + }) + })?; + + let edge_list_indices: IndexVec = d + .read_struct_field("edge_list_indices", 2, |d| { + d.read_seq(|d, len| { + let mut v = IndexVec::with_capacity(len); + for i in 0..len { + v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(v) + }) + })?; + + let edge_list_data: Vec = + d.read_struct_field("edge_list_data", 3, |d| { + d.read_seq(|d, len| { + let mut v = Vec::with_capacity(len); + for i in 0..len { + v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); + } + Ok(v) + }) + })?; + + Ok(SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data }) + }) + } +} diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index 7bc6ae1d1c..1d2bc1a99a 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -15,7 +15,7 @@ pub trait CacheSelector { } pub trait QueryStorage: Default { - type Value; + type Value: Debug; type Stored: Clone; /// Store a value without putting it in the cache. @@ -75,7 +75,7 @@ impl Default for DefaultCache { } } -impl QueryStorage for DefaultCache { +impl QueryStorage for DefaultCache { type Value = V; type Stored = V; @@ -89,7 +89,7 @@ impl QueryStorage for DefaultCache { impl QueryCache for DefaultCache where K: Eq + Hash + Clone + Debug, - V: Clone, + V: Clone + Debug, { type Key = K; type Sharded = FxHashMap; @@ -156,7 +156,7 @@ impl<'tcx, K, V> Default for ArenaCache<'tcx, K, V> { } } -impl<'tcx, K: Eq + Hash, V: 'tcx> QueryStorage for ArenaCache<'tcx, K, V> { +impl<'tcx, K: Eq + Hash, V: Debug + 'tcx> QueryStorage for ArenaCache<'tcx, K, V> { type Value = V; type Stored = &'tcx V; @@ -171,6 +171,7 @@ impl<'tcx, K: Eq + Hash, V: 'tcx> QueryStorage for ArenaCache<'tcx, K, V> { impl<'tcx, K, V: 'tcx> QueryCache for ArenaCache<'tcx, K, V> where K: Eq + Hash + Clone + Debug, + V: Debug, { type Key = K; type Sharded = FxHashMap; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 426f5bb41d..36532135f0 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -20,6 +20,7 @@ use rustc_errors::{Diagnostic, FatalError}; use rustc_span::source_map::DUMMY_SP; use rustc_span::Span; use std::collections::hash_map::Entry; +use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::mem; use std::num::NonZeroU32; @@ -478,7 +479,7 @@ where result } -fn load_from_disk_and_cache_in_memory( +fn load_from_disk_and_cache_in_memory( tcx: CTX, key: K, prev_dep_node_index: SerializedDepNodeIndex, @@ -539,7 +540,7 @@ where #[inline(never)] #[cold] -fn incremental_verify_ich( +fn incremental_verify_ich( tcx: CTX, result: &V, dep_node: &DepNode, @@ -566,7 +567,6 @@ fn incremental_verify_ich( assert!(new_hash == old_hash, "found unstable fingerprints for {:?}", dep_node,); } -#[inline(always)] fn force_query_with_job( tcx: CTX, key: C::Key, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 06e9969697..c4ee4df212 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -16,8 +16,8 @@ use crate::{ use crate::{Module, ModuleData, ModuleKind, NameBinding, NameBindingKind, Segment, ToNameBinding}; use rustc_ast::visit::{self, AssocCtxt, Visitor}; -use rustc_ast::{self as ast, Block, ForeignItem, ForeignItemKind, Item, ItemKind, NodeId}; -use rustc_ast::{AssocItem, AssocItemKind, MetaItemKind, StmtKind}; +use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind}; +use rustc_ast::{Block, FnKind, ForeignItem, ForeignItemKind, ImplKind, Item, ItemKind, NodeId}; use rustc_ast_lowering::ResolverAstLowering; use rustc_attr as attr; use rustc_data_structures::sync::Lrc; @@ -96,7 +96,7 @@ impl<'a> Resolver<'a> { /// Walks up the tree of definitions starting at `def_id`, /// stopping at the first `DefKind::Mod` encountered - fn nearest_mod_parent(&mut self, def_id: DefId) -> Module<'a> { + fn nearest_parent_mod(&mut self, def_id: DefId) -> Module<'a> { let def_key = self.cstore().def_key(def_id); let mut parent_id = DefId { @@ -115,7 +115,7 @@ impl<'a> Resolver<'a> { self.get_module(parent_id) } - crate fn get_module(&mut self, def_id: DefId) -> Module<'a> { + pub fn get_module(&mut self, def_id: DefId) -> Module<'a> { // If this is a local module, it will be in `module_map`, no need to recalculate it. if let Some(def_id) = def_id.as_local() { return self.module_map[&def_id]; @@ -137,7 +137,7 @@ impl<'a> Resolver<'a> { .get_opt_name() .expect("given a DefId that wasn't a module"); - let parent = Some(self.nearest_mod_parent(def_id)); + let parent = Some(self.nearest_parent_mod(def_id)); (name, parent) }; @@ -179,21 +179,21 @@ impl<'a> Resolver<'a> { // so this hopefully won't be a problem. // // See https://github.com/rust-lang/rust/pull/77984#issuecomment-712445508 - self.nearest_mod_parent(def_id) + self.nearest_parent_mod(def_id) } } crate fn get_macro(&mut self, res: Res) -> Option> { match res { - Res::Def(DefKind::Macro(..), def_id) => self.get_macro_by_def_id(def_id), + Res::Def(DefKind::Macro(..), def_id) => Some(self.get_macro_by_def_id(def_id)), Res::NonMacroAttr(attr_kind) => Some(self.non_macro_attr(attr_kind.is_used())), _ => None, } } - crate fn get_macro_by_def_id(&mut self, def_id: DefId) -> Option> { + crate fn get_macro_by_def_id(&mut self, def_id: DefId) -> Lrc { if let Some(ext) = self.macro_map.get(&def_id) { - return Some(ext.clone()); + return ext.clone(); } let ext = Lrc::new(match self.cstore().load_macro_untracked(def_id, &self.session) { @@ -202,7 +202,7 @@ impl<'a> Resolver<'a> { }); self.macro_map.insert(def_id, ext.clone()); - Some(ext) + ext } crate fn build_reduced_graph( @@ -266,7 +266,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } else { // If it's not in an enum, its visibility is restricted to the `mod` item // that it's defined in. - Ok(ty::Visibility::Restricted(self.parent_scope.module.normal_ancestor_id)) + Ok(ty::Visibility::Restricted(self.parent_scope.module.nearest_parent_mod)) } } ast::VisibilityKind::Restricted { ref path, id, .. } => { @@ -342,7 +342,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let field_names = vdata .fields() .iter() - .map(|field| respan(field.span, field.ident.map_or(kw::Invalid, |ident| ident.name))) + .map(|field| respan(field.span, field.ident.map_or(kw::Empty, |ident| ident.name))) .collect(); self.insert_field_names(def_id, field_names); } @@ -525,9 +525,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { ModuleKind::Block(..) => unreachable!(), }; // HACK(eddyb) unclear how good this is, but keeping `$crate` - // in `source` breaks `src/test/compile-fail/import-crate-var.rs`, + // in `source` breaks `src/test/ui/imports/import-crate-var.rs`, // while the current crate doesn't have a valid `crate_name`. - if crate_name != kw::Invalid { + if crate_name != kw::Empty { // `crate_name` should not be interpreted as relative. module_path.push(Segment { ident: Ident { name: kw::PathRoot, span: source.ident.span }, @@ -656,7 +656,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { /// Constructs the reduced graph for one item. fn build_reduced_graph_for_item(&mut self, item: &'b Item) { - if matches!(item.kind, ItemKind::Mod(..)) && item.ident.name == kw::Invalid { + if matches!(item.kind, ItemKind::Mod(..)) && item.ident.name == kw::Empty { // Fake crate root item from expand. return; } @@ -803,7 +803,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let module = self.r.new_module( parent, module_kind, - parent.normal_ancestor_id, + parent.nearest_parent_mod, expansion, item.span, ); @@ -878,7 +878,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let module = self.r.new_module( parent, module_kind, - parent.normal_ancestor_id, + parent.nearest_parent_mod, expansion, item.span, ); @@ -887,7 +887,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } // These items do not add names to modules. - ItemKind::Impl { of_trait: Some(..), .. } => { + ItemKind::Impl(box ImplKind { of_trait: Some(..), .. }) => { self.r.trait_impl_items.insert(local_def_id); } ItemKind::Impl { .. } | ItemKind::ForeignMod(..) | ItemKind::GlobalAsm(..) => {} @@ -921,7 +921,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let module = self.r.new_module( parent, ModuleKind::Block(block.id), - parent.normal_ancestor_id, + parent.nearest_parent_mod, expansion, block.span, ); @@ -1298,26 +1298,31 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { method!(visit_ty: ast::Ty, ast::TyKind::MacCall, walk_ty); fn visit_item(&mut self, item: &'b Item) { - let macro_use = match item.kind { + let orig_module_scope = self.parent_scope.module; + self.parent_scope.macro_rules = match item.kind { ItemKind::MacroDef(..) => { - self.parent_scope.macro_rules = self.define_macro(item); - return; + let macro_rules_scope = self.define_macro(item); + visit::walk_item(self, item); + macro_rules_scope } ItemKind::MacCall(..) => { - self.parent_scope.macro_rules = self.visit_invoc_in_module(item.id); - return; + let macro_rules_scope = self.visit_invoc_in_module(item.id); + visit::walk_item(self, item); + macro_rules_scope + } + _ => { + let orig_macro_rules_scope = self.parent_scope.macro_rules; + self.build_reduced_graph_for_item(item); + visit::walk_item(self, item); + match item.kind { + ItemKind::Mod(..) if self.contains_macro_use(&item.attrs) => { + self.parent_scope.macro_rules + } + _ => orig_macro_rules_scope, + } } - ItemKind::Mod(..) => self.contains_macro_use(&item.attrs), - _ => false, }; - let orig_current_module = self.parent_scope.module; - let orig_current_macro_rules_scope = self.parent_scope.macro_rules; - self.build_reduced_graph_for_item(item); - visit::walk_item(self, item); - self.parent_scope.module = orig_current_module; - if !macro_use { - self.parent_scope.macro_rules = orig_current_macro_rules_scope; - } + self.parent_scope.module = orig_module_scope; } fn visit_stmt(&mut self, stmt: &'b ast::Stmt) { @@ -1366,7 +1371,7 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { AssocCtxt::Trait => { let (def_kind, ns) = match item.kind { AssocItemKind::Const(..) => (DefKind::AssocConst, ValueNS), - AssocItemKind::Fn(_, ref sig, _, _) => { + AssocItemKind::Fn(box FnKind(_, ref sig, _, _)) => { if sig.decl.has_self() { self.r.has_self.insert(def_id); } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 69773ba1d7..727d6ab53d 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -74,7 +74,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { // information we encapsulate into, the better let def_data = match &i.kind { ItemKind::Impl { .. } => DefPathData::Impl, - ItemKind::Mod(..) if i.ident.name == kw::Invalid => { + ItemKind::Mod(..) if i.ident.name == kw::Empty => { // Fake crate root item from expand. return visit::walk_item(self, i); } @@ -91,7 +91,10 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { DefPathData::ValueNs(i.ident.name) } ItemKind::MacroDef(..) => DefPathData::MacroNs(i.ident.name), - ItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), + ItemKind::MacCall(..) => { + visit::walk_item(self, i); + return self.visit_macro_invoc(i.id); + } ItemKind::GlobalAsm(..) => DefPathData::Misc, ItemKind::Use(..) => { return visit::walk_item(self, i); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 809de9beff..3b02f74f2c 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -398,14 +398,30 @@ impl<'a> Resolver<'a> { err.help("use the `|| { ... }` closure form instead"); err } - ResolutionError::AttemptToUseNonConstantValueInConstant => { + ResolutionError::AttemptToUseNonConstantValueInConstant(ident, sugg, current) => { let mut err = struct_span_err!( self.session, span, E0435, "attempt to use a non-constant value in a constant" ); - err.span_label(span, "non-constant value"); + // let foo =... + // ^^^ given this Span + // ------- get this Span to have an applicable suggestion + let sp = + self.session.source_map().span_extend_to_prev_str(ident.span, current, true); + if sp.lo().0 == 0 { + err.span_label(ident.span, &format!("this would need to be a `{}`", sugg)); + } else { + let sp = sp.with_lo(BytePos(sp.lo().0 - current.len() as u32)); + err.span_suggestion( + sp, + &format!("consider using `{}` instead of `{}`", sugg, current), + format!("{} {}", sugg, ident), + Applicability::MaybeIncorrect, + ); + err.span_label(span, "non-constant value"); + } err } ResolutionError::BindingShadowsSomethingUnacceptable(what_binding, name, binding) => { @@ -595,7 +611,8 @@ impl<'a> Resolver<'a> { filter_fn: &impl Fn(Res) -> bool, ) -> Option { let mut suggestions = Vec::new(); - self.visit_scopes(scope_set, parent_scope, ident, |this, scope, use_prelude, _| { + let ctxt = ident.span.ctxt(); + self.visit_scopes(scope_set, parent_scope, ctxt, |this, scope, use_prelude, _| { match scope { Scope::DeriveHelpers(expn_id) => { let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); @@ -666,7 +683,7 @@ impl<'a> Resolver<'a> { )); } Scope::BuiltinAttrs => { - let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin); + let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(kw::Empty)); if filter_fn(res) { suggestions.extend( BUILTIN_ATTRIBUTES @@ -960,7 +977,7 @@ impl<'a> Resolver<'a> { }); if let Some(def_span) = def_span { if span.overlaps(def_span) { - // Don't suggest typo suggestion for itself like in the followoing: + // Don't suggest typo suggestion for itself like in the following: // error[E0423]: expected function, tuple struct or tuple variant, found struct `X` // --> $DIR/issue-64792-bad-unicode-ctor.rs:3:14 // | @@ -1094,10 +1111,9 @@ impl<'a> Resolver<'a> { _, ) = binding.kind { - let def_id = (&*self).parent(ctor_def_id).expect("no parent for a constructor"); + let def_id = self.parent(ctor_def_id).expect("no parent for a constructor"); let fields = self.field_names.get(&def_id)?; - let first_field = fields.first()?; // Handle `struct Foo()` - return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span))); + return fields.iter().map(|name| name.span).reduce(Span::to); // None for `struct Foo()` } None } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b13462587b..e5d6aebe97 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -14,7 +14,6 @@ use crate::{ResolutionError, Resolver, Segment, UseError}; use rustc_ast::ptr::P; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::*; -use rustc_ast::{unwrap_or, walk_list}; use rustc_ast_lowering::ResolverAstLowering; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::DiagnosticId; @@ -92,6 +91,12 @@ crate enum HasGenericParams { No, } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +crate enum ConstantItemKind { + Const, + Static, +} + /// The rib kind restricts certain accesses, /// e.g. to a `Res::Local` of an outer item. #[derive(Copy, Clone, Debug)] @@ -119,7 +124,7 @@ crate enum RibKind<'a> { /// /// The `bool` indicates if this constant may reference generic parameters /// and is used to only allow generic parameters to be used in trivial constant expressions. - ConstantItemRibKind(bool), + ConstantItemRibKind(bool, Option<(Ident, ConstantItemKind)>), /// We passed through a module. ModuleRibKind(Module<'a>), @@ -145,7 +150,7 @@ impl RibKind<'_> { NormalRibKind | ClosureOrAsyncRibKind | FnItemRibKind - | ConstantItemRibKind(_) + | ConstantItemRibKind(..) | ModuleRibKind(_) | MacroDefinition(_) | ConstParamTyRibKind => false, @@ -238,6 +243,13 @@ impl<'a> PathSource<'a> { // "function" here means "anything callable" rather than `DefKind::Fn`, // this is not precise but usually more helpful than just "value". Some(ExprKind::Call(call_expr, _)) => match &call_expr.kind { + // the case of `::some_crate()` + ExprKind::Path(_, path) + if path.segments.len() == 2 + && path.segments[0].ident.name == kw::PathRoot => + { + "external crate" + } ExprKind::Path(_, path) => { let mut msg = "function"; if let Some(segment) = path.segments.iter().last() { @@ -262,52 +274,60 @@ impl<'a> PathSource<'a> { crate fn is_expected(self, res: Res) -> bool { match self { - PathSource::Type => matches!(res, Res::Def( + PathSource::Type => matches!( + res, + Res::Def( DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Trait - | DefKind::TraitAlias - | DefKind::TyAlias - | DefKind::AssocTy - | DefKind::TyParam - | DefKind::OpaqueTy - | DefKind::ForeignTy, + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::TyAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::OpaqueTy + | DefKind::ForeignTy, _, - ) - | Res::PrimTy(..) - | Res::SelfTy(..)), + ) | Res::PrimTy(..) + | Res::SelfTy(..) + ), PathSource::Trait(AliasPossibility::No) => matches!(res, Res::Def(DefKind::Trait, _)), PathSource::Trait(AliasPossibility::Maybe) => { matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) } - PathSource::Expr(..) => matches!(res, Res::Def( + PathSource::Expr(..) => matches!( + res, + Res::Def( DefKind::Ctor(_, CtorKind::Const | CtorKind::Fn) - | DefKind::Const - | DefKind::Static - | DefKind::Fn - | DefKind::AssocFn - | DefKind::AssocConst - | DefKind::ConstParam, + | DefKind::Const + | DefKind::Static + | DefKind::Fn + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::ConstParam, _, - ) - | Res::Local(..) - | Res::SelfCtor(..)), - PathSource::Pat => matches!(res, Res::Def( + ) | Res::Local(..) + | Res::SelfCtor(..) + ), + PathSource::Pat => matches!( + res, + Res::Def( DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst, _, - ) - | Res::SelfCtor(..)), + ) | Res::SelfCtor(..) + ), PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(), - PathSource::Struct => matches!(res, Res::Def( + PathSource::Struct => matches!( + res, + Res::Def( DefKind::Struct - | DefKind::Union - | DefKind::Variant - | DefKind::TyAlias - | DefKind::AssocTy, + | DefKind::Union + | DefKind::Variant + | DefKind::TyAlias + | DefKind::AssocTy, _, - ) - | Res::SelfTy(..)), + ) | Res::SelfTy(..) + ), PathSource::TraitItem(ns) => match res { Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) if ns == ValueNS => true, Res::Def(DefKind::AssocTy, _) if ns == TypeNS => true, @@ -473,8 +493,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } fn visit_foreign_item(&mut self, foreign_item: &'ast ForeignItem) { match foreign_item.kind { - ForeignItemKind::Fn(_, _, ref generics, _) - | ForeignItemKind::TyAlias(_, ref generics, ..) => { + ForeignItemKind::Fn(box FnKind(_, _, ref generics, _)) + | ForeignItemKind::TyAlias(box TyAliasKind(_, ref generics, ..)) => { self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { visit::walk_foreign_item(this, foreign_item); }); @@ -586,7 +606,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // Allow all following defaults to refer to this type parameter. default_ban_rib.bindings.remove(&Ident::with_dummy_span(param.ident.name)); } - GenericParamKind::Const { ref ty, kw_span: _ } => { + GenericParamKind::Const { ref ty, kw_span: _, default: _ } => { + // FIXME(const_generics_defaults): handle `default` value here for bound in ¶m.bounds { self.visit_param_bound(bound); } @@ -633,7 +654,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // Note that we might not be inside of an repeat expression here, // but considering that `IsRepeatExpr` is only relevant for // non-trivial constants this is doesn't matter. - self.with_constant_rib(IsRepeatExpr::No, true, |this| { + self.with_constant_rib(IsRepeatExpr::No, true, None, |this| { this.smart_resolve_path( ty.id, qself.as_ref(), @@ -842,7 +863,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | ClosureOrAsyncRibKind | FnItemRibKind | ItemRibKind(..) - | ConstantItemRibKind(_) + | ConstantItemRibKind(..) | ModuleRibKind(..) | ForwardTyParamBanRibKind | ConstParamTyRibKind => { @@ -917,7 +938,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { debug!("(resolving item) resolving {} ({:?})", name, item.kind); match item.kind { - ItemKind::TyAlias(_, ref generics, _, _) | ItemKind::Fn(_, _, ref generics, _) => { + ItemKind::TyAlias(box TyAliasKind(_, ref generics, _, _)) + | ItemKind::Fn(box FnKind(_, _, ref generics, _)) => { self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { visit::walk_item(this, item) }); @@ -929,17 +951,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.resolve_adt(item, generics); } - ItemKind::Impl { + ItemKind::Impl(box ImplKind { ref generics, ref of_trait, ref self_ty, items: ref impl_items, .. - } => { + }) => { self.resolve_implementation(generics, of_trait, &self_ty, item.id, impl_items); } - ItemKind::Trait(.., ref generics, ref bounds, ref trait_items) => { + ItemKind::Trait(box TraitKind(.., ref generics, ref bounds, ref trait_items)) => { // Create a new rib for the trait-wide type parameters. self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| { let local_def_id = this.r.local_def_id(item.id).to_def_id(); @@ -969,14 +991,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_constant_rib( IsRepeatExpr::No, true, + None, |this| this.visit_expr(expr), ); } } - AssocItemKind::Fn(_, _, generics, _) => { + AssocItemKind::Fn(box FnKind(_, _, generics, _)) => { walk_assoc_item(this, generics, item); } - AssocItemKind::TyAlias(_, generics, _, _) => { + AssocItemKind::TyAlias(box TyAliasKind(_, generics, _, _)) => { walk_assoc_item(this, generics, item); } AssocItemKind::MacCall(_) => { @@ -1011,11 +1034,19 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.with_item_rib(HasGenericParams::No, |this| { this.visit_ty(ty); if let Some(expr) = expr { + let constant_item_kind = match item.kind { + ItemKind::Const(..) => ConstantItemKind::Const, + ItemKind::Static(..) => ConstantItemKind::Static, + _ => unreachable!(), + }; // We already forbid generic params because of the above item rib, // so it doesn't matter whether this is a trivial constant. - this.with_constant_rib(IsRepeatExpr::No, true, |this| { - this.visit_expr(expr) - }); + this.with_constant_rib( + IsRepeatExpr::No, + true, + Some((item.ident, constant_item_kind)), + |this| this.visit_expr(expr), + ); } }); } @@ -1117,15 +1148,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &mut self, is_repeat: IsRepeatExpr, is_trivial: bool, + item: Option<(Ident, ConstantItemKind)>, f: impl FnOnce(&mut Self), ) { debug!("with_constant_rib: is_repeat={:?} is_trivial={}", is_repeat, is_trivial); - self.with_rib(ValueNS, ConstantItemRibKind(is_trivial), |this| { + self.with_rib(ValueNS, ConstantItemRibKind(is_trivial, item), |this| { this.with_rib( TypeNS, - ConstantItemRibKind(is_repeat == IsRepeatExpr::Yes || is_trivial), + ConstantItemRibKind(is_repeat == IsRepeatExpr::Yes || is_trivial, item), |this| { - this.with_label_rib(ConstantItemRibKind(is_trivial), f); + this.with_label_rib(ConstantItemRibKind(is_trivial, item), f); }, ) }); @@ -1151,13 +1183,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { /// When evaluating a `trait` use its associated types' idents for suggestions in E0412. fn with_trait_items( &mut self, - trait_items: &'ast Vec>, + trait_items: &'ast [P], f: impl FnOnce(&mut Self) -> T, ) -> T { - let trait_assoc_items = replace( - &mut self.diagnostic_metadata.current_trait_assoc_items, - Some(&trait_items[..]), - ); + let trait_assoc_items = + replace(&mut self.diagnostic_metadata.current_trait_assoc_items, Some(&trait_items)); let result = f(self); self.diagnostic_metadata.current_trait_assoc_items = trait_assoc_items; result @@ -1267,6 +1297,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_constant_rib( IsRepeatExpr::No, true, + None, |this| { visit::walk_assoc_item( this, @@ -1276,7 +1307,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }, ); } - AssocItemKind::Fn(_, _, generics, _) => { + AssocItemKind::Fn(box FnKind(.., generics, _)) => { // We also need a new scope for the impl item type parameters. this.with_generic_param_rib( generics, @@ -1299,7 +1330,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }, ); } - AssocItemKind::TyAlias(_, generics, _, _) => { + AssocItemKind::TyAlias(box TyAliasKind( + _, + generics, + _, + _, + )) => { // We also need a new scope for the impl item type parameters. this.with_generic_param_rib( generics, @@ -1641,7 +1677,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } // Record as bound if it's valid: - let ident_valid = ident.name != kw::Invalid; + let ident_valid = ident.name != kw::Empty; if ident_valid { bindings.last_mut().unwrap().1.insert(ident); } @@ -1765,7 +1801,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { crate_lint: CrateLint, ) -> PartialRes { tracing::debug!( - "smart_resolve_path_fragment(id={:?},qself={:?},path={:?}", + "smart_resolve_path_fragment(id={:?}, qself={:?}, path={:?})", id, qself, path @@ -1776,7 +1812,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if this.should_report_errs() { let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res); - let def_id = this.parent_scope.module.normal_ancestor_id; + let def_id = this.parent_scope.module.nearest_parent_mod; let instead = res.is_some(); let suggestion = if res.is_none() { this.report_missing_type_error(path) } else { None }; @@ -1805,11 +1841,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Before we start looking for candidates, we have to get our hands // on the type user is trying to perform invocation on; basically: - // we're transforming `HashMap::new` into just `HashMap` - let path = if let Some((_, path)) = path.split_last() { - path - } else { - return Some(parent_err); + // we're transforming `HashMap::new` into just `HashMap`. + let path = match path.split_last() { + Some((_, path)) if !path.is_empty() => path, + _ => return Some(parent_err), }; let (mut err, candidates) = @@ -1844,7 +1879,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { drop(parent_err); - let def_id = this.parent_scope.module.normal_ancestor_id; + let def_id = this.parent_scope.module.nearest_parent_mod; if this.should_report_errs() { this.r.use_injections.push(UseError { @@ -1887,7 +1922,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // it needs to be added to the trait map. if ns == ValueNS { let item_name = path.last().unwrap().ident; - let traits = self.get_traits_containing_item(item_name, ns); + let traits = self.traits_in_scope(item_name, ns); self.r.trait_map.insert(id, traits); } @@ -1901,7 +1936,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { { // Check if we wrote `str::from_utf8` instead of `std::str::from_utf8` let item_span = - path.iter().last().map(|segment| segment.ident.span).unwrap_or(span); + path.iter().last().map_or(span, |segment| segment.ident.span); let mut hm = self.r.session.confused_type_with_std_module.borrow_mut(); hm.insert(item_span, span); @@ -1923,8 +1958,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { _ => report_errors(self, None), }; - if let PathSource::TraitItem(..) = source { - } else { + if !matches!(source, PathSource::TraitItem(..)) { // Avoid recording definition of `A::B` in `::B::C`. self.r.record_partial_res(id, partial_res); } @@ -2201,6 +2235,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.with_constant_rib( is_repeat, constant.value.is_potential_trivial_const_param(), + None, |this| { visit::walk_anon_const(this, constant); }, @@ -2236,6 +2271,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { visit::walk_expr(self, expr); } + ExprKind::Break(None, Some(ref e)) => { + // We use this instead of `visit::walk_expr` to keep the parent expr around for + // better diagnostics. + self.resolve_expr(e, Some(&expr)); + } + ExprKind::Let(ref pat, ref scrutinee) => { self.visit_expr(scrutinee); self.resolve_pattern_top(pat, PatternSource::Let); @@ -2347,12 +2388,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // field, we need to add any trait methods we find that match // the field name so that we can do some nice error reporting // later on in typeck. - let traits = self.get_traits_containing_item(ident, ValueNS); + let traits = self.traits_in_scope(ident, ValueNS); self.r.trait_map.insert(expr.id, traits); } ExprKind::MethodCall(ref segment, ..) => { debug!("(recording candidate traits for expr) recording traits for {}", expr.id); - let traits = self.get_traits_containing_item(segment.ident, ValueNS); + let traits = self.traits_in_scope(segment.ident, ValueNS); self.r.trait_map.insert(expr.id, traits); } _ => { @@ -2361,60 +2402,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } } - fn get_traits_containing_item( - &mut self, - mut ident: Ident, - ns: Namespace, - ) -> Vec { - debug!("(getting traits containing item) looking for '{}'", ident.name); - - let mut found_traits = Vec::new(); - // Look for the current trait. - if let Some((module, _)) = self.current_trait_ref { - if self - .r - .resolve_ident_in_module( - ModuleOrUniformRoot::Module(module), - ident, - ns, - &self.parent_scope, - false, - module.span, - ) - .is_ok() - { - let def_id = module.def_id().unwrap(); - found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] }); - } - } - - ident.span = ident.span.normalize_to_macros_2_0(); - let mut search_module = self.parent_scope.module; - loop { - self.r.get_traits_in_module_containing_item( - ident, - ns, - search_module, - &mut found_traits, - &self.parent_scope, - ); - search_module = - unwrap_or!(self.r.hygienic_lexical_parent(search_module, &mut ident.span), break); - } - - if let Some(prelude) = self.r.prelude { - if !search_module.no_implicit_prelude { - self.r.get_traits_in_module_containing_item( - ident, - ns, - prelude, - &mut found_traits, - &self.parent_scope, - ); - } - } - - found_traits + fn traits_in_scope(&mut self, ident: Ident, ns: Namespace) -> Vec { + self.r.traits_in_scope( + self.current_trait_ref.as_ref().map(|(module, _)| *module), + &self.parent_scope, + ident.span.ctxt(), + Some((ident.name, ns)), + ) } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 68f59baffc..927535b72b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -180,7 +180,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ( format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str), if path_str == "async" && expected.starts_with("struct") { - "`async` blocks are only allowed in the 2018 edition".to_string() + "`async` blocks are only allowed in Rust 2018 or later".to_string() } else { format!("not found in {}", mod_str) }, @@ -264,7 +264,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // The current function has a `self' parameter, but we were unable to resolve // a reference to `self`. This can only happen if the `self` identifier we // are resolving came from a different hygiene context. - if fn_kind.decl().inputs.get(0).map(|p| p.is_self()).unwrap_or(false) { + if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); } else { let doesnt = if is_assoc_fn { @@ -545,17 +545,23 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { if let Some(err_code) = &err.code { if err_code == &rustc_errors::error_code!(E0425) { for label_rib in &self.label_ribs { - for (label_ident, _) in &label_rib.bindings { + for (label_ident, node_id) in &label_rib.bindings { if format!("'{}", ident) == label_ident.to_string() { - let msg = "a label with a similar name exists"; - // FIXME: consider only emitting this suggestion if a label would be valid here - // which is pretty much only the case for `break` expressions. - err.span_suggestion( - span, - &msg, - label_ident.name.to_string(), - Applicability::MaybeIncorrect, - ); + err.span_label(label_ident.span, "a label with a similar name exists"); + if let PathSource::Expr(Some(Expr { + kind: ExprKind::Break(None, Some(_)), + .. + })) = source + { + err.span_suggestion( + span, + "use the similarly named label", + label_ident.name.to_string(), + Applicability::MaybeIncorrect, + ); + // Do not lint against unused label when we suggest them. + self.diagnostic_metadata.unused_labels.remove(node_id); + } } } } @@ -904,7 +910,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { Applicability::MaybeIncorrect, ); if path_str == "try" && span.rust_2015() { - err.note("if you want the `try` keyword, you need to be in the 2018 edition"); + err.note("if you want the `try` keyword, you need Rust 2018 or later"); } } (Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => { @@ -1103,7 +1109,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { if assoc_item.ident == ident { return Some(match &assoc_item.kind { ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst, - ast::AssocItemKind::Fn(_, sig, ..) if sig.decl.has_self() => { + ast::AssocItemKind::Fn(box ast::FnKind(_, sig, ..)) + if sig.decl.has_self() => + { AssocSuggestion::MethodWithSelf } ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn, @@ -1452,8 +1460,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } else { let needs_placeholder = |def_id: DefId, kind: CtorKind| { - let has_no_fields = - self.r.field_names.get(&def_id).map(|f| f.is_empty()).unwrap_or(false); + let has_no_fields = self.r.field_names.get(&def_id).map_or(false, |f| f.is_empty()); match kind { CtorKind::Const => false, CtorKind::Fn | CtorKind::Fictive if has_no_fields => false, @@ -1653,17 +1660,17 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { for missing in &self.missing_named_lifetime_spots { match missing { MissingLifetimeSpot::Generics(generics) => { - let (span, sugg) = if let Some(param) = - generics.params.iter().find(|p| match p.kind { + let (span, sugg) = if let Some(param) = generics.params.iter().find(|p| { + !matches!( + p.kind, hir::GenericParamKind::Type { synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), .. - } => false, - hir::GenericParamKind::Lifetime { + } | hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided, - } => false, - _ => true, - }) { + } + ) + }) { (param.span.shrink_to_lo(), format!("{}, ", lifetime_ref)) } else { suggests_in_band = true; @@ -1842,10 +1849,13 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { msg = "consider introducing a named lifetime parameter".to_string(); should_break = true; if let Some(param) = generics.params.iter().find(|p| { - !matches!(p.kind, hir::GenericParamKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - }) + !matches!( + p.kind, + hir::GenericParamKind::Type { + synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), + .. + } + ) }) { (param.span.shrink_to_lo(), "'a, ".to_string()) } else { @@ -1985,8 +1995,8 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } } - /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics` so - /// this function will emit an error if `min_const_generics` is enabled, the body identified by + /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`. + /// This function will emit an error if `const_generics` is not enabled, the body identified by /// `body_id` is an anonymous constant and `lifetime_ref` is non-static. crate fn maybe_emit_forbidden_non_static_lifetime_error( &self, @@ -2002,7 +2012,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { hir::LifetimeName::Implicit | hir::LifetimeName::Static | hir::LifetimeName::Underscore ); - if self.tcx.features().min_const_generics && is_anon_const && !is_allowed_lifetime { + if !self.tcx.lazy_normalization() && is_anon_const && !is_allowed_lifetime { feature_err( &self.tcx.sess.parse_sess, sym::const_generics, diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 69f28045bb..0bab33976b 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -11,7 +11,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefIdMap, LOCAL_CRATE}; +use rustc_hir::hir_id::ItemLocalId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath}; use rustc_hir::{GenericParamKind, HirIdMap, HirIdSet, LifetimeParamKind}; @@ -20,6 +21,7 @@ use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; +use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use std::borrow::Cow; @@ -284,7 +286,7 @@ pub fn provide(providers: &mut ty::query::Providers) { resolve_lifetimes, named_region_map: |tcx, id| tcx.resolve_lifetimes(LOCAL_CRATE).defs.get(&id), - is_late_bound_map: |tcx, id| tcx.resolve_lifetimes(LOCAL_CRATE).late_bound.get(&id), + is_late_bound_map, object_lifetime_defaults_map: |tcx, id| { tcx.resolve_lifetimes(LOCAL_CRATE).object_lifetime_defaults.get(&id) }, @@ -320,6 +322,32 @@ fn resolve_lifetimes(tcx: TyCtxt<'_>, for_krate: CrateNum) -> ResolveLifetimes { rl } +fn is_late_bound_map<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Option<(LocalDefId, &'tcx FxHashSet)> { + match tcx.def_kind(def_id) { + DefKind::AnonConst => { + let mut def_id = tcx + .parent(def_id.to_def_id()) + .unwrap_or_else(|| bug!("anon const or closure without a parent")); + // We search for the next outer anon const or fn here + // while skipping closures. + // + // Note that for `AnonConst` we still just recurse until we + // find a function body, but who cares :shrug: + while tcx.is_closure(def_id) { + def_id = tcx + .parent(def_id) + .unwrap_or_else(|| bug!("anon const or closure without a parent")); + } + + tcx.is_late_bound_map(def_id.expect_local()) + } + _ => tcx.resolve_lifetimes(LOCAL_CRATE).late_bound.get(&def_id).map(|lt| (def_id, lt)), + } +} + fn krate(tcx: TyCtxt<'_>) -> NamedRegionMap { let krate = tcx.hir().krate(); let mut map = NamedRegionMap { @@ -409,11 +437,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { | hir::ItemKind::Union(_, ref generics) | hir::ItemKind::Trait(_, _, ref generics, ..) | hir::ItemKind::TraitAlias(ref generics, ..) - | hir::ItemKind::Impl { ref generics, .. } => { + | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => { self.missing_named_lifetime_spots.push(generics.into()); // Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name". - // This is not true for other kinds of items.x + // This is not true for other kinds of items. let track_lifetime_uses = matches!(item.kind, hir::ItemKind::Impl { .. }); // These kinds of items have only early-bound lifetime parameters. let mut index = if sub_items_have_self_param(&item.kind) { @@ -644,17 +672,17 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } else { bug!(); }; - if let hir::ParamName::Plain(param_name) = name { - if param_name.name == kw::UnderscoreLifetime { - // Pick the elided lifetime "definition" if one exists - // and use it to make an elision scope. - self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); - elision = Some(reg); - } else { - lifetimes.insert(name, reg); - } + // We cannot predict what lifetimes are unused in opaque type. + self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); + if let hir::ParamName::Plain(Ident { + name: kw::UnderscoreLifetime, + .. + }) = name + { + // Pick the elided lifetime "definition" if one exists + // and use it to make an elision scope. + elision = Some(reg); } else { - self.lifetime_uses.insert(def_id, LifetimeUseSet::Many); lifetimes.insert(name, reg); } } @@ -1145,7 +1173,7 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body<'_>) { } fn expression_label(ex: &hir::Expr<'_>) -> Option { - if let hir::ExprKind::Loop(_, Some(label), _) = ex.kind { Some(label.ident) } else { None } + if let hir::ExprKind::Loop(_, Some(label), ..) = ex.kind { Some(label.ident) } else { None } } fn check_if_label_shadows_lifetime(tcx: TyCtxt<'_>, mut scope: ScopeRef<'_>, label: Ident) { @@ -1370,12 +1398,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { fn lifetime_deletion_span(&self, name: Ident, generics: &hir::Generics<'_>) -> Option { generics.params.iter().enumerate().find_map(|(i, param)| { if param.name.ident() == name { - let mut in_band = false; - if let hir::GenericParamKind::Lifetime { kind } = param.kind { - if let hir::LifetimeParamKind::InBand = kind { - in_band = true; - } - } + let in_band = matches!( + param.kind, + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::InBand } + ); if in_band { Some(param.span) } else if generics.params.len() == 1 { @@ -1405,12 +1431,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { lifetime: &hir::Lifetime, ) { let name = lifetime.name.ident(); - let mut remove_decl = None; - if let Some(parent_def_id) = self.tcx.parent(def_id) { - if let Some(generics) = self.tcx.hir().get_generics(parent_def_id) { - remove_decl = self.lifetime_deletion_span(name, generics); - } - } + let remove_decl = self + .tcx + .parent(def_id) + .and_then(|parent_def_id| self.tcx.hir().get_generics(parent_def_id)) + .and_then(|generics| self.lifetime_deletion_span(name, generics)); let mut remove_use = None; let mut elide_use = None; @@ -1433,7 +1458,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { hir::TyKind::Path(ref qpath) => { if let QPath::Resolved(_, path) = qpath { let last_segment = &path.segments[path.segments.len() - 1]; - let generics = last_segment.generic_args(); + let generics = last_segment.args(); for arg in generics.args.iter() { if let GenericArg::Lifetime(lt) = arg { if lt.name.ident() == name { @@ -1677,7 +1702,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } match parent.kind { hir::ItemKind::Trait(_, _, ref generics, ..) - | hir::ItemKind::Impl { ref generics, .. } => { + | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => { index += generics.params.len() as u32; } _ => {} @@ -1769,8 +1794,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let result = loop { match *scope { Scope::Body { id, s } => { - // Non-static lifetimes are prohibited in anonymous constants under - // `min_const_generics`. + // Non-static lifetimes are prohibited in anonymous constants without + // `const_generics`. self.maybe_emit_forbidden_non_static_lifetime_error(id, lifetime_ref); outermost_body = Some(id); @@ -2058,18 +2083,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { output: Option<&'tcx hir::Ty<'tcx>>, ) { debug!("visit_fn_like_elision: enter"); - let mut arg_elide = Elide::FreshLateAnon(Cell::new(0)); - let arg_scope = Scope::Elision { elide: arg_elide.clone(), s: self.scope }; + let arg_scope = Scope::Elision { elide: Elide::FreshLateAnon(Cell::new(0)), s: self.scope }; self.with(arg_scope, |_, this| { for input in inputs { this.visit_ty(input); } - match *this.scope { - Scope::Elision { ref elide, .. } => { - arg_elide = elide.clone(); - } - _ => bug!(), - } }); let output = match output { @@ -2102,7 +2120,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body), .. }) => { - if let hir::ItemKind::Impl { ref self_ty, ref items, .. } = + if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) = self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind { impl_self = Some(self_ty); @@ -2397,7 +2415,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { _ => break, } } - break Some(e); + break Some(&e[..]); } Elide::Forbid => break None, }; @@ -2427,7 +2445,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { lifetime_refs.len(), &lifetime_names, lifetime_spans, - error.map(|p| &p[..]).unwrap_or(&[]), + error.unwrap_or(&[]), ); err.emit(); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index f764fbc3f8..b19990e49b 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -9,6 +9,7 @@ //! Type-relative name resolution (methods, fields, associated items) happens in `librustc_typeck`. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(box_patterns)] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(format_args_capture)] @@ -33,7 +34,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::ptr_key::PtrKey; use rustc_data_structures::sync::Lrc; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; -use rustc_expand::base::SyntaxExtension; +use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_hir::def::Namespace::*; use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; @@ -44,12 +45,13 @@ use rustc_index::vec::IndexVec; use rustc_metadata::creader::{CStore, CrateLoader}; use rustc_middle::hir::exports::ExportMap; use rustc_middle::middle::cstore::{CrateStore, MetadataLoaderDyn}; +use rustc_middle::span_bug; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, DefIdTree, ResolverOutputs}; -use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; +use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnId, ExpnKind, MacroKind, SyntaxContext, Transparency}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -64,7 +66,7 @@ use tracing::debug; use diagnostics::{extend_span_to_previous_binding, find_span_of_binding_until_next_binding}; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use imports::{Import, ImportKind, ImportResolver, NameResolution}; -use late::{HasGenericParams, PathSource, Rib, RibKind::*}; +use late::{ConstantItemKind, HasGenericParams, PathSource, Rib, RibKind::*}; use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; type Res = def::Res; @@ -210,11 +212,15 @@ enum ResolutionError<'a> { /// Error E0434: can't capture dynamic environment in a fn item. CannotCaptureDynamicEnvironmentInFnItem, /// Error E0435: attempt to use a non-constant value in a constant. - AttemptToUseNonConstantValueInConstant, + AttemptToUseNonConstantValueInConstant( + Ident, + /* suggestion */ &'static str, + /* current */ &'static str, + ), /// Error E0530: `X` bindings cannot shadow `Y`s. BindingShadowsSomethingUnacceptable(&'static str, Symbol, &'a NameBinding<'a>), /// Error E0128: type parameters with a default cannot use forward-declared identifiers. - ForwardDeclaredTyParam, // FIXME(const_generics:defaults) + ForwardDeclaredTyParam, // FIXME(const_generics_defaults) /// ERROR E0770: the type of const parameters must not depend on other generic parameters. ParamInTyOfConstParam(Symbol), /// constant values inside of type parameter defaults must not depend on generic parameters. @@ -422,7 +428,9 @@ enum ModuleKind { /// /// This could be: /// - /// * A normal module ‒ either `mod from_file;` or `mod from_block { }`. + /// * A normal module – either `mod from_file;` or `mod from_block { }` – + /// or the crate root (which is conceptually a top-level module). + /// Note that the crate root's [name][Self::name] will be [`kw::Empty`]. /// * A trait or an enum (it implicitly contains associated types, methods and variant /// constructors). Def(DefKind, DefId, Symbol), @@ -456,28 +464,42 @@ struct BindingKey { type Resolutions<'a> = RefCell>>>; /// One node in the tree of modules. +/// +/// Note that a "module" in resolve is broader than a `mod` that you declare in Rust code. It may be one of these: +/// +/// * `mod` +/// * crate root (aka, top-level anonymous module) +/// * `enum` +/// * `trait` +/// * curly-braced block with statements +/// +/// You can use [`ModuleData::kind`] to determine the kind of module this is. pub struct ModuleData<'a> { + /// The direct parent module (it may not be a `mod`, however). parent: Option>, + /// What kind of module this is, because this may not be a `mod`. kind: ModuleKind, - // The def id of the closest normal module (`mod`) ancestor (including this module). - normal_ancestor_id: DefId, + /// The [`DefId`] of the nearest `mod` item ancestor (which may be this module). + /// This may be the crate root. + nearest_parent_mod: DefId, - // Mapping between names and their (possibly in-progress) resolutions in this module. - // Resolutions in modules from other crates are not populated until accessed. + /// Mapping between names and their (possibly in-progress) resolutions in this module. + /// Resolutions in modules from other crates are not populated until accessed. lazy_resolutions: Resolutions<'a>, - // True if this is a module from other crate that needs to be populated on access. + /// True if this is a module from other crate that needs to be populated on access. populate_on_access: Cell, - // Macro invocations that can expand into items in this module. + /// Macro invocations that can expand into items in this module. unexpanded_invocations: RefCell>, + /// Whether `#[no_implicit_prelude]` is active. no_implicit_prelude: bool, glob_importers: RefCell>>, globs: RefCell>>, - // Used to memoize the traits in this module for faster searches through all traits in scope. + /// Used to memoize the traits in this module for faster searches through all traits in scope. traits: RefCell)]>>>, /// Span of the module itself. Used for error reporting. @@ -492,16 +514,16 @@ impl<'a> ModuleData<'a> { fn new( parent: Option>, kind: ModuleKind, - normal_ancestor_id: DefId, + nearest_parent_mod: DefId, expansion: ExpnId, span: Span, ) -> Self { ModuleData { parent, kind, - normal_ancestor_id, + nearest_parent_mod, lazy_resolutions: Default::default(), - populate_on_access: Cell::new(!normal_ancestor_id.is_local()), + populate_on_access: Cell::new(!nearest_parent_mod.is_local()), unexpanded_invocations: Default::default(), no_implicit_prelude: false, glob_importers: RefCell::new(Vec::new()), @@ -743,10 +765,13 @@ impl<'a> NameBinding<'a> { } fn is_variant(&self) -> bool { - matches!(self.kind, NameBindingKind::Res( + matches!( + self.kind, + NameBindingKind::Res( Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _), _, - )) + ) + ) } fn is_extern_crate(&self) -> bool { @@ -850,7 +875,7 @@ pub struct ExternPreludeEntry<'a> { /// Used for better errors for E0773 enum BuiltinMacroState { - NotYetSeen(SyntaxExtension), + NotYetSeen(SyntaxExtensionKind), AlreadySeen(Span), } @@ -1028,7 +1053,7 @@ pub struct ResolverArenas<'a> { impl<'a> ResolverArenas<'a> { fn alloc_module(&'a self, module: ModuleData<'a>) -> Module<'a> { let module = self.modules.alloc(module); - if module.def_id().map(|def_id| def_id.is_local()).unwrap_or(true) { + if module.def_id().map_or(true, |def_id| def_id.is_local()) { self.local_modules.borrow_mut().push(module); } module @@ -1182,12 +1207,12 @@ impl<'a> Resolver<'a> { ) -> Resolver<'a> { let root_local_def_id = LocalDefId { local_def_index: CRATE_DEF_INDEX }; let root_def_id = root_local_def_id.to_def_id(); - let root_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Invalid); + let root_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Empty); let graph_root = arenas.alloc_module(ModuleData { no_implicit_prelude: session.contains_name(&krate.attrs, sym::no_implicit_prelude), ..ModuleData::new(None, root_module_kind, root_def_id, ExpnId::root(), krate.span) }); - let empty_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Invalid); + let empty_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Empty); let empty_module = arenas.alloc_module(ModuleData { no_implicit_prelude: true, ..ModuleData::new( @@ -1427,7 +1452,7 @@ impl<'a> Resolver<'a> { } fn is_builtin_macro(&mut self, res: Res) -> bool { - self.get_macro(res).map_or(false, |ext| ext.is_builtin) + self.get_macro(res).map_or(false, |ext| ext.builtin_name.is_some()) } fn macro_def(&self, mut ctxt: SyntaxContext) -> DefId { @@ -1441,61 +1466,86 @@ impl<'a> Resolver<'a> { /// Entry point to crate resolution. pub fn resolve_crate(&mut self, krate: &Crate) { - let _prof_timer = self.session.prof.generic_activity("resolve_crate"); + self.session.time("resolve_crate", || { + self.session.time("finalize_imports", || ImportResolver { r: self }.finalize_imports()); + self.session.time("finalize_macro_resolutions", || self.finalize_macro_resolutions()); + self.session.time("late_resolve_crate", || self.late_resolve_crate(krate)); + self.session.time("resolve_check_unused", || self.check_unused(krate)); + self.session.time("resolve_report_errors", || self.report_errors(krate)); + self.session.time("resolve_postprocess", || self.crate_loader.postprocess(krate)); + }); + } - ImportResolver { r: self }.finalize_imports(); - self.finalize_macro_resolutions(); + pub fn traits_in_scope( + &mut self, + current_trait: Option>, + parent_scope: &ParentScope<'a>, + ctxt: SyntaxContext, + assoc_item: Option<(Symbol, Namespace)>, + ) -> Vec { + let mut found_traits = Vec::new(); + + if let Some(module) = current_trait { + if self.trait_may_have_item(Some(module), assoc_item) { + let def_id = module.def_id().unwrap(); + found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] }); + } + } - self.late_resolve_crate(krate); + self.visit_scopes(ScopeSet::All(TypeNS, false), parent_scope, ctxt, |this, scope, _, _| { + match scope { + Scope::Module(module) => { + this.traits_in_module(module, assoc_item, &mut found_traits); + } + Scope::StdLibPrelude => { + if let Some(module) = this.prelude { + this.traits_in_module(module, assoc_item, &mut found_traits); + } + } + Scope::ExternPrelude | Scope::ToolPrelude | Scope::BuiltinTypes => {} + _ => unreachable!(), + } + None::<()> + }); - self.check_unused(krate); - self.report_errors(krate); - self.crate_loader.postprocess(krate); + found_traits } - fn get_traits_in_module_containing_item( + fn traits_in_module( &mut self, - ident: Ident, - ns: Namespace, module: Module<'a>, + assoc_item: Option<(Symbol, Namespace)>, found_traits: &mut Vec, - parent_scope: &ParentScope<'a>, ) { - assert!(ns == TypeNS || ns == ValueNS); module.ensure_traits(self); let traits = module.traits.borrow(); + for (trait_name, trait_binding) in traits.as_ref().unwrap().iter() { + if self.trait_may_have_item(trait_binding.module(), assoc_item) { + let def_id = trait_binding.res().def_id(); + let import_ids = self.find_transitive_imports(&trait_binding.kind, *trait_name); + found_traits.push(TraitCandidate { def_id, import_ids }); + } + } + } - for &(trait_name, binding) in traits.as_ref().unwrap().iter() { - // Traits have pseudo-modules that can be used to search for the given ident. - if let Some(module) = binding.module() { - let mut ident = ident; - if ident.span.glob_adjust(module.expansion, binding.span).is_none() { - continue; - } - if self - .resolve_ident_in_module_unadjusted( - ModuleOrUniformRoot::Module(module), - ident, - ns, - parent_scope, - false, - module.span, - ) - .is_ok() - { - let import_ids = self.find_transitive_imports(&binding.kind, trait_name); - let trait_def_id = module.def_id().unwrap(); - found_traits.push(TraitCandidate { def_id: trait_def_id, import_ids }); - } - } else if let Res::Def(DefKind::TraitAlias, _) = binding.res() { - // For now, just treat all trait aliases as possible candidates, since we don't - // know if the ident is somewhere in the transitive bounds. - let import_ids = self.find_transitive_imports(&binding.kind, trait_name); - let trait_def_id = binding.res().def_id(); - found_traits.push(TraitCandidate { def_id: trait_def_id, import_ids }); - } else { - bug!("candidate is not trait or trait alias?") + // List of traits in scope is pruned on best effort basis. We reject traits not having an + // associated item with the given name and namespace (if specified). This is a conservative + // optimization, proper hygienic type-based resolution of associated items is done in typeck. + // We don't reject trait aliases (`trait_module == None`) because we don't have access to their + // associated items. + fn trait_may_have_item( + &mut self, + trait_module: Option>, + assoc_item: Option<(Symbol, Namespace)>, + ) -> bool { + match (trait_module, assoc_item) { + (Some(trait_module), Some((name, ns))) => { + self.resolutions(trait_module).borrow().iter().any(|resolution| { + let (&BindingKey { ident: assoc_ident, ns: assoc_ns, .. }, _) = resolution; + assoc_ns == ns && assoc_ident.name == name + }) } + _ => true, } } @@ -1519,11 +1569,11 @@ impl<'a> Resolver<'a> { &self, parent: Module<'a>, kind: ModuleKind, - normal_ancestor_id: DefId, + nearest_parent_mod: DefId, expn_id: ExpnId, span: Span, ) -> Module<'a> { - let module = ModuleData::new(Some(parent), kind, normal_ancestor_id, expn_id, span); + let module = ModuleData::new(Some(parent), kind, nearest_parent_mod, expn_id, span); self.arenas.alloc_module(module) } @@ -1610,8 +1660,13 @@ impl<'a> Resolver<'a> { &mut self, scope_set: ScopeSet, parent_scope: &ParentScope<'a>, - ident: Ident, - mut visitor: impl FnMut(&mut Self, Scope<'a>, /*use_prelude*/ bool, Ident) -> Option, + ctxt: SyntaxContext, + mut visitor: impl FnMut( + &mut Self, + Scope<'a>, + /*use_prelude*/ bool, + SyntaxContext, + ) -> Option, ) -> Option { // General principles: // 1. Not controlled (user-defined) names should have higher priority than controlled names @@ -1654,7 +1709,7 @@ impl<'a> Resolver<'a> { // 4c. Standard library prelude (de-facto closed, controlled). // 6. Language prelude: builtin attributes (closed, controlled). - let rust_2015 = ident.span.rust_2015(); + let rust_2015 = ctxt.edition() == Edition::Edition2015; let (ns, macro_kind, is_absolute_path) = match scope_set { ScopeSet::All(ns, _) => (ns, None, false), ScopeSet::AbsolutePath(ns) => (ns, None, true), @@ -1667,7 +1722,7 @@ impl<'a> Resolver<'a> { TypeNS | ValueNS => Scope::Module(module), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; - let mut ident = ident.normalize_to_macros_2_0(); + let mut ctxt = ctxt.normalize_to_macros_2_0(); let mut use_prelude = !module.no_implicit_prelude; loop { @@ -1703,7 +1758,7 @@ impl<'a> Resolver<'a> { }; if visit { - if let break_result @ Some(..) = visitor(self, scope, use_prelude, ident) { + if let break_result @ Some(..) = visitor(self, scope, use_prelude, ctxt) { return break_result; } } @@ -1733,17 +1788,17 @@ impl<'a> Resolver<'a> { }, Scope::CrateRoot => match ns { TypeNS => { - ident.span.adjust(ExpnId::root()); + ctxt.adjust(ExpnId::root()); Scope::ExternPrelude } ValueNS | MacroNS => break, }, Scope::Module(module) => { use_prelude = !module.no_implicit_prelude; - match self.hygienic_lexical_parent(module, &mut ident.span) { + match self.hygienic_lexical_parent(module, &mut ctxt) { Some(parent_module) => Scope::Module(parent_module), None => { - ident.span.adjust(ExpnId::root()); + ctxt.adjust(ExpnId::root()); match ns { TypeNS => Scope::ExternPrelude, ValueNS => Scope::StdLibPrelude, @@ -1797,7 +1852,7 @@ impl<'a> Resolver<'a> { ribs: &[Rib<'a>], ) -> Option> { assert!(ns == TypeNS || ns == ValueNS); - if ident.name == kw::Invalid { + if ident.name == kw::Empty { return Some(LexicalScopeBinding::Res(Res::Err)); } let (general_span, normalized_span) = if ident.name == kw::SelfUpper { @@ -1821,14 +1876,16 @@ impl<'a> Resolver<'a> { // Use the rib kind to determine whether we are resolving parameters // (macro 2.0 hygiene) or local variables (`macro_rules` hygiene). let rib_ident = if ribs[i].kind.contains_params() { normalized_ident } else { ident }; - if let Some(res) = ribs[i].bindings.get(&rib_ident).cloned() { + if let Some((original_rib_ident_def, res)) = ribs[i].bindings.get_key_value(&rib_ident) + { // The ident resolves to a type parameter or local variable. return Some(LexicalScopeBinding::Res(self.validate_res_from_ribs( i, rib_ident, - res, + *res, record_used, path_span, + *original_rib_ident_def, ribs, ))); } @@ -1866,16 +1923,18 @@ impl<'a> Resolver<'a> { ident = normalized_ident; let mut poisoned = None; loop { + let mut span_data = ident.span.data(); let opt_module = if let Some(node_id) = record_used_id { self.hygienic_lexical_parent_with_compatibility_fallback( module, - &mut ident.span, + &mut span_data.ctxt, node_id, &mut poisoned, ) } else { - self.hygienic_lexical_parent(module, &mut ident.span) + self.hygienic_lexical_parent(module, &mut span_data.ctxt) }; + ident.span = span_data.span(); module = unwrap_or!(opt_module, break); let adjusted_parent_scope = &ParentScope { module, ..*parent_scope }; let result = self.resolve_ident_in_module_unadjusted( @@ -1949,10 +2008,10 @@ impl<'a> Resolver<'a> { fn hygienic_lexical_parent( &mut self, module: Module<'a>, - span: &mut Span, + ctxt: &mut SyntaxContext, ) -> Option> { - if !module.expansion.outer_expn_is_descendant_of(span.ctxt()) { - return Some(self.macro_def_scope(span.remove_mark())); + if !module.expansion.outer_expn_is_descendant_of(*ctxt) { + return Some(self.macro_def_scope(ctxt.remove_mark())); } if let ModuleKind::Block(..) = module.kind { @@ -1965,11 +2024,11 @@ impl<'a> Resolver<'a> { fn hygienic_lexical_parent_with_compatibility_fallback( &mut self, module: Module<'a>, - span: &mut Span, + ctxt: &mut SyntaxContext, node_id: NodeId, poisoned: &mut Option, ) -> Option> { - if let module @ Some(..) = self.hygienic_lexical_parent(module, span) { + if let module @ Some(..) = self.hygienic_lexical_parent(module, ctxt) { return module; } @@ -1991,14 +2050,13 @@ impl<'a> Resolver<'a> { { // The macro is a proc macro derive if let Some(def_id) = module.expansion.expn_data().macro_def_id { - if let Some(ext) = self.get_macro_by_def_id(def_id) { - if !ext.is_builtin - && ext.macro_kind() == MacroKind::Derive - && parent.expansion.outer_expn_is_descendant_of(span.ctxt()) - { - *poisoned = Some(node_id); - return module.parent; - } + let ext = self.get_macro_by_def_id(def_id); + if ext.builtin_name.is_none() + && ext.macro_kind() == MacroKind::Derive + && parent.expansion.outer_expn_is_descendant_of(*ctxt) + { + *poisoned = Some(node_id); + return module.parent; } } } @@ -2117,7 +2175,7 @@ impl<'a> Resolver<'a> { return self.graph_root; } }; - let module = self.get_module(DefId { index: CRATE_DEF_INDEX, ..module.normal_ancestor_id }); + let module = self.get_module(DefId { index: CRATE_DEF_INDEX, ..module.nearest_parent_mod }); debug!( "resolve_crate_root({:?}): got module {:?} ({:?}) (ident.span = {:?})", ident, @@ -2129,10 +2187,10 @@ impl<'a> Resolver<'a> { } fn resolve_self(&mut self, ctxt: &mut SyntaxContext, module: Module<'a>) -> Module<'a> { - let mut module = self.get_module(module.normal_ancestor_id); + let mut module = self.get_module(module.nearest_parent_mod); while module.span.ctxt().normalize_to_macros_2_0() != *ctxt { let parent = module.parent.unwrap_or_else(|| self.macro_def_scope(ctxt.remove_mark())); - module = self.get_module(parent.normal_ancestor_id); + module = self.get_module(parent.nearest_parent_mod); } module } @@ -2416,15 +2474,24 @@ impl<'a> Resolver<'a> { } else if i == 0 { if ident .name - .with(|n| n.chars().next().map_or(false, |c| c.is_ascii_uppercase())) + .as_str() + .chars() + .next() + .map_or(false, |c| c.is_ascii_uppercase()) { (format!("use of undeclared type `{}`", ident), None) } else { (format!("use of undeclared crate or module `{}`", ident), None) } } else { - let mut msg = - format!("could not find `{}` in `{}`", ident, path[i - 1].ident); + let parent = path[i - 1].ident.name; + let parent = if parent == kw::PathRoot { + "crate root".to_owned() + } else { + format!("`{}`", parent) + }; + + let mut msg = format!("could not find `{}` in {}", ident, parent); if ns == TypeNS || ns == ValueNS { let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; if let FindBindingResult::Binding(Ok(binding)) = @@ -2432,11 +2499,11 @@ impl<'a> Resolver<'a> { { let mut found = |what| { msg = format!( - "expected {}, found {} `{}` in `{}`", + "expected {}, found {} `{}` in {}", ns.descr(), what, ident, - path[i - 1].ident + parent ) }; if binding.module().is_some() { @@ -2538,6 +2605,7 @@ impl<'a> Resolver<'a> { mut res: Res, record_used: bool, span: Span, + original_rib_ident_def: Ident, all_ribs: &[Rib<'a>], ) -> Res { const CG_BUG_STR: &str = "min_const_generics resolve check didn't stop compilation"; @@ -2584,10 +2652,32 @@ impl<'a> Resolver<'a> { res_err = Some(CannotCaptureDynamicEnvironmentInFnItem); } } - ConstantItemRibKind(_) => { + ConstantItemRibKind(_, item) => { // Still doesn't deal with upvars if record_used { - self.report_error(span, AttemptToUseNonConstantValueInConstant); + let (span, resolution_error) = + if let Some((ident, constant_item_kind)) = item { + let kind_str = match constant_item_kind { + ConstantItemKind::Const => "const", + ConstantItemKind::Static => "static", + }; + ( + span, + AttemptToUseNonConstantValueInConstant( + ident, "let", kind_str, + ), + ) + } else { + ( + rib_ident.span, + AttemptToUseNonConstantValueInConstant( + original_rib_ident_def, + "const", + "let", + ), + ) + }; + self.report_error(span, resolution_error); } return Res::Err; } @@ -2623,9 +2713,13 @@ impl<'a> Resolver<'a> { in_ty_param_default = true; continue; } - ConstantItemRibKind(trivial) => { + ConstantItemRibKind(trivial, _) => { + let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. - if !trivial && self.session.features_untracked().min_const_generics { + if !(trivial + || features.const_generics + || features.lazy_normalization_consts) + { // HACK(min_const_generics): If we encounter `Self` in an anonymous constant // we can't easily tell if it's generic at this stage, so we instead remember // this and then enforce the self type to be concrete later on. @@ -2712,9 +2806,13 @@ impl<'a> Resolver<'a> { in_ty_param_default = true; continue; } - ConstantItemRibKind(trivial) => { + ConstantItemRibKind(trivial, _) => { + let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. - if !trivial && self.session.features_untracked().min_const_generics { + if !(trivial + || features.const_generics + || features.lazy_normalization_consts) + { if record_used { self.report_error( span, @@ -2783,7 +2881,7 @@ impl<'a> Resolver<'a> { } fn is_accessible_from(&self, vis: ty::Visibility, module: Module<'a>) -> bool { - vis.is_accessible_from(module.normal_ancestor_id, self) + vis.is_accessible_from(module.nearest_parent_mod, self) } fn set_binding_parent_module(&mut self, binding: &'a NameBinding<'a>, module: Module<'a>) { @@ -2807,7 +2905,7 @@ impl<'a> Resolver<'a> { self.binding_parent_modules.get(&PtrKey(modularized)), ) { (Some(macro_rules), Some(modularized)) => { - macro_rules.normal_ancestor_id == modularized.normal_ancestor_id + macro_rules.nearest_parent_mod == modularized.nearest_parent_mod && modularized.is_ancestor_of(macro_rules) } _ => false, @@ -2965,7 +3063,7 @@ impl<'a> Resolver<'a> { let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); let from_item = - self.extern_prelude.get(&ident).map(|entry| entry.introduced_by_item).unwrap_or(true); + self.extern_prelude.get(&ident).map_or(true, |entry| entry.introduced_by_item); // Only suggest removing an import if both bindings are to the same def, if both spans // aren't dummy spans. Further, if both bindings are imports, then the ident must have // been introduced by a item. @@ -3161,34 +3259,6 @@ impl<'a> Resolver<'a> { }) } - /// This is equivalent to `get_traits_in_module_containing_item`, but without filtering by the associated item. - /// - /// This is used by rustdoc for intra-doc links. - pub fn traits_in_scope(&mut self, module_id: DefId) -> Vec { - let module = self.get_module(module_id); - module.ensure_traits(self); - let traits = module.traits.borrow(); - let to_candidate = - |this: &mut Self, &(trait_name, binding): &(Ident, &NameBinding<'_>)| TraitCandidate { - def_id: binding.res().def_id(), - import_ids: this.find_transitive_imports(&binding.kind, trait_name), - }; - - let mut candidates: Vec<_> = - traits.as_ref().unwrap().iter().map(|x| to_candidate(self, x)).collect(); - - if let Some(prelude) = self.prelude { - if !module.no_implicit_prelude { - prelude.ensure_traits(self); - candidates.extend( - prelude.traits.borrow().as_ref().unwrap().iter().map(|x| to_candidate(self, x)), - ); - } - } - - candidates - } - /// Rustdoc uses this to resolve things in a recoverable way. `ResolutionError<'a>` /// isn't something that can be returned because it can't be made to live that long, /// and also it's a private type. Fortunately rustdoc doesn't need to know the error, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index c8dbe68512..d0adee2429 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -14,7 +14,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::ptr_key::PtrKey; use rustc_data_structures::sync::Lrc; use rustc_errors::struct_span_err; -use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand, SyntaxExtension}; +use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand}; +use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{AstFragment, Invocation, InvocationKind}; use rustc_feature::is_builtin_attr_name; @@ -160,7 +161,7 @@ impl<'a> ResolverExpand for Resolver<'a> { hygiene::update_dollar_crate_names(|ctxt| { let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt)); match self.resolve_crate_root(ident).kind { - ModuleKind::Def(.., name) if name != kw::Invalid => name, + ModuleKind::Def(.., name) if name != kw::Empty => name, _ => kw::Crate, } }); @@ -176,10 +177,11 @@ impl<'a> ResolverExpand for Resolver<'a> { parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); } - fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension) { - if self.builtin_macros.insert(ident.name, BuiltinMacroState::NotYetSeen(ext)).is_some() { + fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind) { + if self.builtin_macros.insert(name, BuiltinMacroState::NotYetSeen(ext)).is_some() { self.session - .span_err(ident.span, &format!("built-in macro `{}` was already defined", ident)); + .diagnostic() + .bug(&format!("built-in macro `{}` was already registered", name)); } } @@ -285,7 +287,7 @@ impl<'a> ResolverExpand for Resolver<'a> { helper_attrs.extend( ext.helper_attrs.iter().map(|name| Ident::new(*name, span)), ); - if ext.is_derive_copy { + if ext.builtin_name == Some(sym::Copy) { self.containers_deriving_copy.insert(invoc_id); } ext @@ -328,7 +330,7 @@ impl<'a> ResolverExpand for Resolver<'a> { if after_derive { self.session.span_err(span, "macro attributes must be placed before `#[derive]`"); } - let normal_module_def_id = self.macro_def_scope(invoc_id).normal_ancestor_id; + let normal_module_def_id = self.macro_def_scope(invoc_id).nearest_parent_mod; self.definitions.add_parent_module_of_macro_def(invoc_id, normal_module_def_id); } @@ -342,6 +344,8 @@ impl<'a> ResolverExpand for Resolver<'a> { } fn lint_node_id(&mut self, expn_id: ExpnId) -> NodeId { + // FIXME - make this more precise. This currently returns the NodeId of the + // nearest closing item - we should try to return the closest parent of the ExpnId self.invocation_parents .get(&expn_id) .map_or(ast::CRATE_NODE_ID, |id| self.def_id_to_node_id[*id]) @@ -618,8 +622,9 @@ impl<'a> Resolver<'a> { let break_result = self.visit_scopes( scope_set, parent_scope, - orig_ident, - |this, scope, use_prelude, ident| { + orig_ident.span.ctxt(), + |this, scope, use_prelude, ctxt| { + let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt)); let ok = |res, span, arenas| { Ok(( (res, ty::Visibility::Public, span, ExpnId::root()).to_name_binding(arenas), @@ -754,7 +759,11 @@ impl<'a> Resolver<'a> { } Scope::BuiltinAttrs => { if is_builtin_attr_name(ident.name) { - ok(Res::NonMacroAttr(NonMacroAttrKind::Builtin), DUMMY_SP, this.arenas) + ok( + Res::NonMacroAttr(NonMacroAttrKind::Builtin(ident.name)), + DUMMY_SP, + this.arenas, + ) } else { Err(Determinacy::Determined) } @@ -807,13 +816,15 @@ impl<'a> Resolver<'a> { // Found another solution, if the first one was "weak", report an error. let (res, innermost_res) = (binding.res(), innermost_binding.res()); if res != innermost_res { - let builtin = Res::NonMacroAttr(NonMacroAttrKind::Builtin); + let is_builtin = |res| { + matches!(res, Res::NonMacroAttr(NonMacroAttrKind::Builtin(..))) + }; let derive_helper_compat = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat); let ambiguity_error_kind = if is_import { Some(AmbiguityKind::Import) - } else if innermost_res == builtin || res == builtin { + } else if is_builtin(innermost_res) || is_builtin(res) { Some(AmbiguityKind::BuiltinAttr) } else if innermost_res == derive_helper_compat || res == derive_helper_compat @@ -1089,14 +1100,14 @@ impl<'a> Resolver<'a> { edition, ); - if result.is_builtin { + if let Some(builtin_name) = result.builtin_name { // The macro was marked with `#[rustc_builtin_macro]`. - if let Some(builtin_macro) = self.builtin_macros.get_mut(&item.ident.name) { + if let Some(builtin_macro) = self.builtin_macros.get_mut(&builtin_name) { // The macro is a built-in, replace its expander function // while still taking everything else from the source code. // If we already loaded this builtin macro, give a better error message than 'no such builtin macro'. match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) { - BuiltinMacroState::NotYetSeen(ext) => result.kind = ext.kind, + BuiltinMacroState::NotYetSeen(ext) => result.kind = ext, BuiltinMacroState::AlreadySeen(span) => { struct_span_err!( self.session, diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index 40d60a8394..2834e7b632 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -582,7 +582,7 @@ impl<'tcx> DumpVisitor<'tcx> { } ref v => { let mut value = format!("{}::{}", enum_data.name, name); - if let &hir::VariantData::Tuple(ref fields, _) = v { + if let hir::VariantData::Tuple(fields, _) = v { value.push('('); value.push_str( &fields @@ -631,14 +631,7 @@ impl<'tcx> DumpVisitor<'tcx> { self.dumper.dump_def(&access, enum_data); } - fn process_impl( - &mut self, - item: &'tcx hir::Item<'tcx>, - generics: &'tcx hir::Generics<'tcx>, - trait_ref: &'tcx Option>, - typ: &'tcx hir::Ty<'tcx>, - impl_items: &'tcx [hir::ImplItemRef<'tcx>], - ) { + fn process_impl(&mut self, item: &'tcx hir::Item<'tcx>, impl_: &'tcx hir::Impl<'tcx>) { if let Some(impl_data) = self.save_ctxt.get_item_data(item) { if !self.span.filter_generated(item.span) { if let super::Data::RelationData(rel, imp) = impl_data { @@ -652,12 +645,12 @@ impl<'tcx> DumpVisitor<'tcx> { let map = &self.tcx.hir(); self.nest_typeck_results(map.local_def_id(item.hir_id), |v| { - v.visit_ty(&typ); - if let &Some(ref trait_ref) = trait_ref { + v.visit_ty(&impl_.self_ty); + if let Some(trait_ref) = &impl_.of_trait { v.process_path(trait_ref.hir_ref_id, &hir::QPath::Resolved(None, &trait_ref.path)); } - v.process_generic_params(generics, "", item.hir_id); - for impl_item in impl_items { + v.process_generic_params(&impl_.generics, "", item.hir_id); + for impl_item in impl_.items { v.process_impl_item( map.impl_item(impl_item.id), map.local_def_id(item.hir_id).to_def_id(), @@ -1082,7 +1075,7 @@ impl<'tcx> DumpVisitor<'tcx> { ); } - if let &Some(ref default_ty) = default_ty { + if let Some(default_ty) = default_ty { self.visit_ty(default_ty) } } @@ -1287,9 +1280,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { self.process_struct(item, def, ty_params) } hir::ItemKind::Enum(ref def, ref ty_params) => self.process_enum(item, def, ty_params), - hir::ItemKind::Impl { ref generics, ref of_trait, ref self_ty, ref items, .. } => { - self.process_impl(item, generics, of_trait, &self_ty, items) - } + hir::ItemKind::Impl(ref impl_) => self.process_impl(item, impl_), hir::ItemKind::Trait(_, _, ref generics, ref trait_refs, methods) => { self.process_trait(item, generics, trait_refs, methods) } @@ -1343,9 +1334,12 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { self.visit_ty(ty); } } - hir::GenericParamKind::Const { ref ty } => { + hir::GenericParamKind::Const { ref ty, ref default } => { self.process_bounds(param.bounds); self.visit_ty(ty); + if let Some(default) = default { + self.visit_anon_const(default); + } } } } diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index 056c0b3d9d..129123349a 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -318,7 +318,7 @@ impl<'tcx> SaveContext<'tcx> { attributes: lower_attributes(item.attrs.to_vec(), self), })) } - hir::ItemKind::Impl { ref of_trait, ref self_ty, ref items, .. } => { + hir::ItemKind::Impl(hir::Impl { ref of_trait, ref self_ty, ref items, .. }) => { if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = self_ty.kind { // Common case impl for a struct or something basic. if generated_code(path.span) { @@ -410,7 +410,7 @@ impl<'tcx> SaveContext<'tcx> { match self.tcx.impl_of_method(def_id) { Some(impl_id) => match self.tcx.hir().get_if_local(impl_id) { Some(Node::Item(item)) => match item.kind { - hir::ItemKind::Impl { ref self_ty, .. } => { + hir::ItemKind::Impl(hir::Impl { ref self_ty, .. }) => { let hir = self.tcx.hir(); let mut qualname = String::from("<"); @@ -670,7 +670,7 @@ impl<'tcx> SaveContext<'tcx> { ) -> Option { // Returns true if the path is function type sugar, e.g., `Fn(A) -> B`. fn fn_type(seg: &hir::PathSegment<'_>) -> bool { - seg.args.map(|args| args.parenthesized).unwrap_or(false) + seg.args.map_or(false, |args| args.parenthesized) } let res = self.get_path_res(id); diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index ff445d727f..8ada7e34fe 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -501,7 +501,7 @@ impl<'hir> Sig for hir::Item<'hir> { Ok(sig) } - hir::ItemKind::Impl { + hir::ItemKind::Impl(hir::Impl { unsafety, polarity, defaultness, @@ -511,7 +511,7 @@ impl<'hir> Sig for hir::Item<'hir> { ref of_trait, ref self_ty, items: _, - } => { + }) => { let mut text = String::new(); if let hir::Defaultness::Default { .. } = defaultness { text.push_str("default "); @@ -614,9 +614,12 @@ impl<'hir> Sig for hir::Generics<'hir> { start: offset + text.len(), end: offset + text.len() + param_text.as_str().len(), }); - if let hir::GenericParamKind::Const { ref ty } = param.kind { + if let hir::GenericParamKind::Const { ref ty, ref default } = param.kind { param_text.push_str(": "); param_text.push_str(&ty_to_string(&ty)); + if let Some(ref _default) = default { + // FIXME(const_generics_defaults): push the `default` value here + } } if !param.bounds.is_empty() { param_text.push_str(": "); diff --git a/compiler/rustc_serialize/src/collection_impls.rs b/compiler/rustc_serialize/src/collection_impls.rs index 3d274cb015..ae6d27e037 100644 --- a/compiler/rustc_serialize/src/collection_impls.rs +++ b/compiler/rustc_serialize/src/collection_impls.rs @@ -11,12 +11,8 @@ use smallvec::{Array, SmallVec}; impl>> Encodable for SmallVec { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.len(), |s| { - for (i, e) in self.iter().enumerate() { - s.emit_seq_elt(i, |s| e.encode(s))?; - } - Ok(()) - }) + let slice: &[A::Item] = self; + slice.encode(s) } } @@ -292,46 +288,28 @@ where impl> Encodable for Rc<[T]> { fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.emit_seq(self.len(), |s| { - for (index, e) in self.iter().enumerate() { - s.emit_seq_elt(index, |s| e.encode(s))?; - } - Ok(()) - }) + let slice: &[T] = self; + slice.encode(s) } } impl> Decodable for Rc<[T]> { fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut vec = Vec::with_capacity(len); - for index in 0..len { - vec.push(d.read_seq_elt(index, |d| Decodable::decode(d))?); - } - Ok(vec.into()) - }) + let vec: Vec = Decodable::decode(d)?; + Ok(vec.into()) } } impl> Encodable for Arc<[T]> { fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.emit_seq(self.len(), |s| { - for (index, e) in self.iter().enumerate() { - s.emit_seq_elt(index, |s| e.encode(s))?; - } - Ok(()) - }) + let slice: &[T] = self; + slice.encode(s) } } impl> Decodable for Arc<[T]> { fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut vec = Vec::with_capacity(len); - for index in 0..len { - vec.push(d.read_seq_elt(index, |d| Decodable::decode(d))?); - } - Ok(vec.into()) - }) + let vec: Vec = Decodable::decode(d)?; + Ok(vec.into()) } } diff --git a/compiler/rustc_serialize/src/leb128.rs b/compiler/rustc_serialize/src/leb128.rs index 1fe6a309e9..ea2df80e64 100644 --- a/compiler/rustc_serialize/src/leb128.rs +++ b/compiler/rustc_serialize/src/leb128.rs @@ -1,16 +1,45 @@ +#![macro_use] + +macro_rules! max_leb128_len { + ($int_ty:ty) => { + // The longest LEB128 encoding for an integer uses 7 bits per byte. + (std::mem::size_of::<$int_ty>() * 8 + 6) / 7 + }; +} + +// Returns the longest LEB128 encoding of all supported integer types. +pub const fn max_leb128_len() -> usize { + max_leb128_len!(u128) +} + macro_rules! impl_write_unsigned_leb128 { - ($fn_name:ident, $int_ty:ident) => { + ($fn_name:ident, $int_ty:ty) => { #[inline] - pub fn $fn_name(out: &mut Vec, mut value: $int_ty) { + pub fn $fn_name( + out: &mut [::std::mem::MaybeUninit; max_leb128_len!($int_ty)], + mut value: $int_ty, + ) -> &[u8] { + let mut i = 0; + loop { if value < 0x80 { - out.push(value as u8); + unsafe { + *out.get_unchecked_mut(i).as_mut_ptr() = value as u8; + } + + i += 1; break; } else { - out.push(((value & 0x7f) | 0x80) as u8); + unsafe { + *out.get_unchecked_mut(i).as_mut_ptr() = ((value & 0x7f) | 0x80) as u8; + } + value >>= 7; + i += 1; } } + + unsafe { ::std::mem::MaybeUninit::slice_assume_init_ref(&out.get_unchecked(..i)) } } }; } @@ -22,7 +51,7 @@ impl_write_unsigned_leb128!(write_u128_leb128, u128); impl_write_unsigned_leb128!(write_usize_leb128, usize); macro_rules! impl_read_unsigned_leb128 { - ($fn_name:ident, $int_ty:ident) => { + ($fn_name:ident, $int_ty:ty) => { #[inline] pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) { let mut result = 0; @@ -49,62 +78,79 @@ impl_read_unsigned_leb128!(read_u64_leb128, u64); impl_read_unsigned_leb128!(read_u128_leb128, u128); impl_read_unsigned_leb128!(read_usize_leb128, usize); -#[inline] -/// encodes an integer using signed leb128 encoding and stores -/// the result using a callback function. -/// -/// The callback `write` is called once for each position -/// that is to be written to with the byte to be encoded -/// at that position. -pub fn write_signed_leb128_to(mut value: i128, mut write: W) -where - W: FnMut(u8), -{ - loop { - let mut byte = (value as u8) & 0x7f; - value >>= 7; - let more = - !(((value == 0) && ((byte & 0x40) == 0)) || ((value == -1) && ((byte & 0x40) != 0))); - - if more { - byte |= 0x80; // Mark this byte to show that more bytes will follow. - } +macro_rules! impl_write_signed_leb128 { + ($fn_name:ident, $int_ty:ty) => { + #[inline] + pub fn $fn_name( + out: &mut [::std::mem::MaybeUninit; max_leb128_len!($int_ty)], + mut value: $int_ty, + ) -> &[u8] { + let mut i = 0; + + loop { + let mut byte = (value as u8) & 0x7f; + value >>= 7; + let more = !(((value == 0) && ((byte & 0x40) == 0)) + || ((value == -1) && ((byte & 0x40) != 0))); - write(byte); + if more { + byte |= 0x80; // Mark this byte to show that more bytes will follow. + } + + unsafe { + *out.get_unchecked_mut(i).as_mut_ptr() = byte; + } + + i += 1; + + if !more { + break; + } + } - if !more { - break; + unsafe { ::std::mem::MaybeUninit::slice_assume_init_ref(&out.get_unchecked(..i)) } } - } + }; } -#[inline] -pub fn write_signed_leb128(out: &mut Vec, value: i128) { - write_signed_leb128_to(value, |v| out.push(v)) -} +impl_write_signed_leb128!(write_i16_leb128, i16); +impl_write_signed_leb128!(write_i32_leb128, i32); +impl_write_signed_leb128!(write_i64_leb128, i64); +impl_write_signed_leb128!(write_i128_leb128, i128); +impl_write_signed_leb128!(write_isize_leb128, isize); -#[inline] -pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) { - let mut result = 0; - let mut shift = 0; - let mut position = start_position; - let mut byte; - - loop { - byte = data[position]; - position += 1; - result |= i128::from(byte & 0x7F) << shift; - shift += 7; - - if (byte & 0x80) == 0 { - break; - } - } +macro_rules! impl_read_signed_leb128 { + ($fn_name:ident, $int_ty:ty) => { + #[inline] + pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) { + let mut result = 0; + let mut shift = 0; + let mut position = 0; + let mut byte; - if (shift < 64) && ((byte & 0x40) != 0) { - // sign extend - result |= -(1 << shift); - } + loop { + byte = slice[position]; + position += 1; + result |= <$int_ty>::from(byte & 0x7F) << shift; + shift += 7; - (result, position - start_position) + if (byte & 0x80) == 0 { + break; + } + } + + if (shift < <$int_ty>::BITS) && ((byte & 0x40) != 0) { + // sign extend + result |= (!0 << shift); + } + + (result, position) + } + }; } + +impl_read_signed_leb128!(read_i16_leb128, i16); +impl_read_signed_leb128!(read_i32_leb128, i32); +impl_read_signed_leb128!(read_i64_leb128, i64); +impl_read_signed_leb128!(read_i128_leb128, i128); +impl_read_signed_leb128!(read_isize_leb128, isize); diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index fab29f29e8..53c3adcc20 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -13,7 +13,13 @@ Core encoding and decoding interfaces. #![feature(never_type)] #![feature(nll)] #![feature(associated_type_bounds)] -#![feature(min_const_generics)] +#![cfg_attr(bootstrap, feature(min_const_generics))] +#![feature(min_specialization)] +#![feature(vec_spare_capacity)] +#![feature(core_intrinsics)] +#![feature(int_bits_const)] +#![feature(maybe_uninit_slice)] +#![feature(new_uninit)] #![cfg_attr(test, feature(test))] #![allow(rustc::internal)] diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index 8b79c93e76..3e37fc87ce 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -1,6 +1,11 @@ -use crate::leb128::{self, read_signed_leb128, write_signed_leb128}; +use crate::leb128::{self, max_leb128_len}; use crate::serialize; use std::borrow::Cow; +use std::fs::File; +use std::io::{self, Write}; +use std::mem::MaybeUninit; +use std::path::Path; +use std::ptr; // ----------------------------------------------------------------------------- // Encoder @@ -21,22 +26,35 @@ impl Encoder { self.data } + #[inline] + pub fn position(&self) -> usize { + self.data.len() + } + #[inline] pub fn emit_raw_bytes(&mut self, s: &[u8]) { self.data.extend_from_slice(s); } } -macro_rules! write_uleb128 { - ($enc:expr, $value:expr, $fun:ident) => {{ - leb128::$fun(&mut $enc.data, $value); - Ok(()) - }}; -} +macro_rules! write_leb128 { + ($enc:expr, $value:expr, $int_ty:ty, $fun:ident) => {{ + const MAX_ENCODED_LEN: usize = max_leb128_len!($int_ty); + let old_len = $enc.data.len(); + + if MAX_ENCODED_LEN > $enc.data.capacity() - old_len { + $enc.data.reserve(MAX_ENCODED_LEN); + } + + // SAFETY: The above check and `reserve` ensures that there is enough + // room to write the encoded value to the vector's internal buffer. + unsafe { + let buf = &mut *($enc.data.as_mut_ptr().add(old_len) + as *mut [MaybeUninit; MAX_ENCODED_LEN]); + let encoded = leb128::$fun(buf, $value); + $enc.data.set_len(old_len + encoded.len()); + } -macro_rules! write_sleb128 { - ($enc:expr, $value:expr) => {{ - write_signed_leb128(&mut $enc.data, $value as i128); Ok(()) }}; } @@ -51,27 +69,27 @@ impl serialize::Encoder for Encoder { #[inline] fn emit_usize(&mut self, v: usize) -> EncodeResult { - write_uleb128!(self, v, write_usize_leb128) + write_leb128!(self, v, usize, write_usize_leb128) } #[inline] fn emit_u128(&mut self, v: u128) -> EncodeResult { - write_uleb128!(self, v, write_u128_leb128) + write_leb128!(self, v, u128, write_u128_leb128) } #[inline] fn emit_u64(&mut self, v: u64) -> EncodeResult { - write_uleb128!(self, v, write_u64_leb128) + write_leb128!(self, v, u64, write_u64_leb128) } #[inline] fn emit_u32(&mut self, v: u32) -> EncodeResult { - write_uleb128!(self, v, write_u32_leb128) + write_leb128!(self, v, u32, write_u32_leb128) } #[inline] fn emit_u16(&mut self, v: u16) -> EncodeResult { - write_uleb128!(self, v, write_u16_leb128) + write_leb128!(self, v, u16, write_u16_leb128) } #[inline] @@ -82,27 +100,27 @@ impl serialize::Encoder for Encoder { #[inline] fn emit_isize(&mut self, v: isize) -> EncodeResult { - write_sleb128!(self, v) + write_leb128!(self, v, isize, write_isize_leb128) } #[inline] fn emit_i128(&mut self, v: i128) -> EncodeResult { - write_sleb128!(self, v) + write_leb128!(self, v, i128, write_i128_leb128) } #[inline] fn emit_i64(&mut self, v: i64) -> EncodeResult { - write_sleb128!(self, v) + write_leb128!(self, v, i64, write_i64_leb128) } #[inline] fn emit_i32(&mut self, v: i32) -> EncodeResult { - write_sleb128!(self, v) + write_leb128!(self, v, i32, write_i32_leb128) } #[inline] fn emit_i16(&mut self, v: i16) -> EncodeResult { - write_sleb128!(self, v) + write_leb128!(self, v, i16, write_i16_leb128) } #[inline] @@ -141,10 +159,354 @@ impl serialize::Encoder for Encoder { } } -impl Encoder { +pub type FileEncodeResult = Result<(), io::Error>; + +// `FileEncoder` encodes data to file via fixed-size buffer. +// +// When encoding large amounts of data to a file, using `FileEncoder` may be +// preferred over using `Encoder` to encode to a `Vec`, and then writing the +// `Vec` to file, as the latter uses as much memory as there is encoded data, +// while the former uses the fixed amount of memory allocated to the buffer. +// `FileEncoder` also has the advantage of not needing to reallocate as data +// is appended to it, but the disadvantage of requiring more error handling, +// which has some runtime overhead. +pub struct FileEncoder { + // The input buffer. For adequate performance, we need more control over + // buffering than `BufWriter` offers. If `BufWriter` ever offers a raw + // buffer access API, we can use it, and remove `buf` and `buffered`. + buf: Box<[MaybeUninit]>, + buffered: usize, + flushed: usize, + file: File, +} + +impl FileEncoder { + pub fn new>(path: P) -> io::Result { + const DEFAULT_BUF_SIZE: usize = 8192; + FileEncoder::with_capacity(path, DEFAULT_BUF_SIZE) + } + + pub fn with_capacity>(path: P, capacity: usize) -> io::Result { + // Require capacity at least as large as the largest LEB128 encoding + // here, so that we don't have to check or handle this on every write. + assert!(capacity >= max_leb128_len()); + + // Require capacity small enough such that some capacity checks can be + // done using guaranteed non-overflowing add rather than sub, which + // shaves an instruction off those code paths (on x86 at least). + assert!(capacity <= usize::MAX - max_leb128_len()); + + let file = File::create(path)?; + + Ok(FileEncoder { buf: Box::new_uninit_slice(capacity), buffered: 0, flushed: 0, file }) + } + #[inline] pub fn position(&self) -> usize { - self.data.len() + // Tracking position this way instead of having a `self.position` field + // means that we don't have to update the position on every write call. + self.flushed + self.buffered + } + + #[inline] + pub fn emit_raw_bytes(&mut self, s: &[u8]) -> FileEncodeResult { + self.write_all(s) + } + + pub fn flush(&mut self) -> FileEncodeResult { + // This is basically a copy of `BufWriter::flush`. If `BufWriter` ever + // offers a raw buffer access API, we can use it, and remove this. + + /// Helper struct to ensure the buffer is updated after all the writes + /// are complete. It tracks the number of written bytes and drains them + /// all from the front of the buffer when dropped. + struct BufGuard<'a> { + buffer: &'a mut [u8], + encoder_buffered: &'a mut usize, + encoder_flushed: &'a mut usize, + flushed: usize, + } + + impl<'a> BufGuard<'a> { + fn new( + buffer: &'a mut [u8], + encoder_buffered: &'a mut usize, + encoder_flushed: &'a mut usize, + ) -> Self { + assert_eq!(buffer.len(), *encoder_buffered); + Self { buffer, encoder_buffered, encoder_flushed, flushed: 0 } + } + + /// The unwritten part of the buffer + fn remaining(&self) -> &[u8] { + &self.buffer[self.flushed..] + } + + /// Flag some bytes as removed from the front of the buffer + fn consume(&mut self, amt: usize) { + self.flushed += amt; + } + + /// true if all of the bytes have been written + fn done(&self) -> bool { + self.flushed >= *self.encoder_buffered + } + } + + impl Drop for BufGuard<'_> { + fn drop(&mut self) { + if self.flushed > 0 { + if self.done() { + *self.encoder_flushed += *self.encoder_buffered; + *self.encoder_buffered = 0; + } else { + self.buffer.copy_within(self.flushed.., 0); + *self.encoder_flushed += self.flushed; + *self.encoder_buffered -= self.flushed; + } + } + } + } + + let mut guard = BufGuard::new( + unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[..self.buffered]) }, + &mut self.buffered, + &mut self.flushed, + ); + + while !guard.done() { + match self.file.write(guard.remaining()) { + Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write the buffered data", + )); + } + Ok(n) => guard.consume(n), + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + + Ok(()) + } + + #[inline] + fn capacity(&self) -> usize { + self.buf.len() + } + + #[inline] + fn write_one(&mut self, value: u8) -> FileEncodeResult { + // We ensure this during `FileEncoder` construction. + debug_assert!(self.capacity() >= 1); + + let mut buffered = self.buffered; + + if std::intrinsics::unlikely(buffered >= self.capacity()) { + self.flush()?; + buffered = 0; + } + + // SAFETY: The above check and `flush` ensures that there is enough + // room to write the input to the buffer. + unsafe { + *MaybeUninit::slice_as_mut_ptr(&mut self.buf).add(buffered) = value; + } + + self.buffered = buffered + 1; + + Ok(()) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> FileEncodeResult { + let capacity = self.capacity(); + let buf_len = buf.len(); + + if std::intrinsics::likely(buf_len <= capacity) { + let mut buffered = self.buffered; + + if std::intrinsics::unlikely(buf_len > capacity - buffered) { + self.flush()?; + buffered = 0; + } + + // SAFETY: The above check and `flush` ensures that there is enough + // room to write the input to the buffer. + unsafe { + let src = buf.as_ptr(); + let dst = MaybeUninit::slice_as_mut_ptr(&mut self.buf).add(buffered); + ptr::copy_nonoverlapping(src, dst, buf_len); + } + + self.buffered = buffered + buf_len; + + Ok(()) + } else { + self.write_all_unbuffered(buf) + } + } + + fn write_all_unbuffered(&mut self, mut buf: &[u8]) -> FileEncodeResult { + if self.buffered > 0 { + self.flush()?; + } + + // This is basically a copy of `Write::write_all` but also updates our + // `self.flushed`. It's necessary because `Write::write_all` does not + // return the number of bytes written when an error is encountered, and + // without that, we cannot accurately update `self.flushed` on error. + while !buf.is_empty() { + match self.file.write(buf) { + Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer", + )); + } + Ok(n) => { + buf = &buf[n..]; + self.flushed += n; + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + + Ok(()) + } +} + +impl Drop for FileEncoder { + fn drop(&mut self) { + let _result = self.flush(); + } +} + +macro_rules! file_encoder_write_leb128 { + ($enc:expr, $value:expr, $int_ty:ty, $fun:ident) => {{ + const MAX_ENCODED_LEN: usize = max_leb128_len!($int_ty); + + // We ensure this during `FileEncoder` construction. + debug_assert!($enc.capacity() >= MAX_ENCODED_LEN); + + let mut buffered = $enc.buffered; + + // This can't overflow. See assertion in `FileEncoder::with_capacity`. + if std::intrinsics::unlikely(buffered + MAX_ENCODED_LEN > $enc.capacity()) { + $enc.flush()?; + buffered = 0; + } + + // SAFETY: The above check and flush ensures that there is enough + // room to write the encoded value to the buffer. + let buf = unsafe { + &mut *($enc.buf.as_mut_ptr().add(buffered) as *mut [MaybeUninit; MAX_ENCODED_LEN]) + }; + + let encoded = leb128::$fun(buf, $value); + $enc.buffered = buffered + encoded.len(); + + Ok(()) + }}; +} + +impl serialize::Encoder for FileEncoder { + type Error = io::Error; + + #[inline] + fn emit_unit(&mut self) -> FileEncodeResult { + Ok(()) + } + + #[inline] + fn emit_usize(&mut self, v: usize) -> FileEncodeResult { + file_encoder_write_leb128!(self, v, usize, write_usize_leb128) + } + + #[inline] + fn emit_u128(&mut self, v: u128) -> FileEncodeResult { + file_encoder_write_leb128!(self, v, u128, write_u128_leb128) + } + + #[inline] + fn emit_u64(&mut self, v: u64) -> FileEncodeResult { + file_encoder_write_leb128!(self, v, u64, write_u64_leb128) + } + + #[inline] + fn emit_u32(&mut self, v: u32) -> FileEncodeResult { + file_encoder_write_leb128!(self, v, u32, write_u32_leb128) + } + + #[inline] + fn emit_u16(&mut self, v: u16) -> FileEncodeResult { + file_encoder_write_leb128!(self, v, u16, write_u16_leb128) + } + + #[inline] + fn emit_u8(&mut self, v: u8) -> FileEncodeResult { + self.write_one(v) + } + + #[inline] + fn emit_isize(&mut self, v: isize) -> FileEncodeResult { + file_encoder_write_leb128!(self, v, isize, write_isize_leb128) + } + + #[inline] + fn emit_i128(&mut self, v: i128) -> FileEncodeResult { + file_encoder_write_leb128!(self, v, i128, write_i128_leb128) + } + + #[inline] + fn emit_i64(&mut self, v: i64) -> FileEncodeResult { + file_encoder_write_leb128!(self, v, i64, write_i64_leb128) + } + + #[inline] + fn emit_i32(&mut self, v: i32) -> FileEncodeResult { + file_encoder_write_leb128!(self, v, i32, write_i32_leb128) + } + + #[inline] + fn emit_i16(&mut self, v: i16) -> FileEncodeResult { + file_encoder_write_leb128!(self, v, i16, write_i16_leb128) + } + + #[inline] + fn emit_i8(&mut self, v: i8) -> FileEncodeResult { + let as_u8: u8 = unsafe { std::mem::transmute(v) }; + self.emit_u8(as_u8) + } + + #[inline] + fn emit_bool(&mut self, v: bool) -> FileEncodeResult { + self.emit_u8(if v { 1 } else { 0 }) + } + + #[inline] + fn emit_f64(&mut self, v: f64) -> FileEncodeResult { + let as_u64: u64 = v.to_bits(); + self.emit_u64(as_u64) + } + + #[inline] + fn emit_f32(&mut self, v: f32) -> FileEncodeResult { + let as_u32: u32 = v.to_bits(); + self.emit_u32(as_u32) + } + + #[inline] + fn emit_char(&mut self, v: char) -> FileEncodeResult { + self.emit_u32(v as u32) + } + + #[inline] + fn emit_str(&mut self, v: &str) -> FileEncodeResult { + self.emit_usize(v.len())?; + self.emit_raw_bytes(v.as_bytes()) } } @@ -179,11 +541,19 @@ impl<'a> Decoder<'a> { } #[inline] - pub fn read_raw_bytes(&mut self, s: &mut [u8]) -> Result<(), String> { + pub fn read_raw_bytes(&mut self, s: &mut [MaybeUninit]) -> Result<(), String> { let start = self.position; let end = start + s.len(); + assert!(end <= self.data.len()); - s.copy_from_slice(&self.data[start..end]); + // SAFETY: Both `src` and `dst` point to at least `s.len()` elements: + // `src` points to at least `s.len()` elements by above assert, and + // `dst` points to `s.len()` elements by derivation from `s`. + unsafe { + let src = self.data.as_ptr().add(start); + let dst = s.as_mut_ptr() as *mut u8; + ptr::copy_nonoverlapping(src, dst, s.len()); + } self.position = end; @@ -191,7 +561,7 @@ impl<'a> Decoder<'a> { } } -macro_rules! read_uleb128 { +macro_rules! read_leb128 { ($dec:expr, $fun:ident) => {{ let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]); $dec.position += bytes_read; @@ -199,14 +569,6 @@ macro_rules! read_uleb128 { }}; } -macro_rules! read_sleb128 { - ($dec:expr, $t:ty) => {{ - let (value, bytes_read) = read_signed_leb128($dec.data, $dec.position); - $dec.position += bytes_read; - Ok(value as $t) - }}; -} - impl<'a> serialize::Decoder for Decoder<'a> { type Error = String; @@ -217,22 +579,22 @@ impl<'a> serialize::Decoder for Decoder<'a> { #[inline] fn read_u128(&mut self) -> Result { - read_uleb128!(self, read_u128_leb128) + read_leb128!(self, read_u128_leb128) } #[inline] fn read_u64(&mut self) -> Result { - read_uleb128!(self, read_u64_leb128) + read_leb128!(self, read_u64_leb128) } #[inline] fn read_u32(&mut self) -> Result { - read_uleb128!(self, read_u32_leb128) + read_leb128!(self, read_u32_leb128) } #[inline] fn read_u16(&mut self) -> Result { - read_uleb128!(self, read_u16_leb128) + read_leb128!(self, read_u16_leb128) } #[inline] @@ -244,27 +606,27 @@ impl<'a> serialize::Decoder for Decoder<'a> { #[inline] fn read_usize(&mut self) -> Result { - read_uleb128!(self, read_usize_leb128) + read_leb128!(self, read_usize_leb128) } #[inline] fn read_i128(&mut self) -> Result { - read_sleb128!(self, i128) + read_leb128!(self, read_i128_leb128) } #[inline] fn read_i64(&mut self) -> Result { - read_sleb128!(self, i64) + read_leb128!(self, read_i64_leb128) } #[inline] fn read_i32(&mut self) -> Result { - read_sleb128!(self, i32) + read_leb128!(self, read_i32_leb128) } #[inline] fn read_i16(&mut self) -> Result { - read_sleb128!(self, i16) + read_leb128!(self, read_i16_leb128) } #[inline] @@ -276,7 +638,7 @@ impl<'a> serialize::Decoder for Decoder<'a> { #[inline] fn read_isize(&mut self) -> Result { - read_sleb128!(self, isize) + read_leb128!(self, read_isize_leb128) } #[inline] @@ -316,3 +678,43 @@ impl<'a> serialize::Decoder for Decoder<'a> { err.to_string() } } + +// Specializations for contiguous byte sequences follow. The default implementations for slices +// encode and decode each element individually. This isn't necessary for `u8` slices when using +// opaque encoders and decoders, because each `u8` is unchanged by encoding and decoding. +// Therefore, we can use more efficient implementations that process the entire sequence at once. + +// Specialize encoding byte slices. This specialization also applies to encoding `Vec`s, etc., +// since the default implementations call `encode` on their slices internally. +impl serialize::Encodable for [u8] { + fn encode(&self, e: &mut Encoder) -> EncodeResult { + serialize::Encoder::emit_usize(e, self.len())?; + e.emit_raw_bytes(self); + Ok(()) + } +} + +impl serialize::Encodable for [u8] { + fn encode(&self, e: &mut FileEncoder) -> FileEncodeResult { + serialize::Encoder::emit_usize(e, self.len())?; + e.emit_raw_bytes(self) + } +} + +// Specialize decoding `Vec`. This specialization also applies to decoding `Box<[u8]>`s, etc., +// since the default implementations call `decode` to produce a `Vec` internally. +impl<'a> serialize::Decodable> for Vec { + fn decode(d: &mut Decoder<'a>) -> Result { + let len = serialize::Decoder::read_usize(d)?; + + let mut v = Vec::with_capacity(len); + let buf = &mut v.spare_capacity_mut()[..len]; + d.read_raw_bytes(buf)?; + + unsafe { + v.set_len(len); + } + + Ok(v) + } +} diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index aa305f3c7f..47aad5b88c 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -527,7 +527,7 @@ impl> Decodable for Rc { } impl> Encodable for [T] { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { + default fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_seq(self.len(), |s| { for (i, e) in self.iter().enumerate() { s.emit_seq_elt(i, |s| e.encode(s))? @@ -545,7 +545,7 @@ impl> Encodable for Vec { } impl> Decodable for Vec { - fn decode(d: &mut D) -> Result, D::Error> { + default fn decode(d: &mut D) -> Result, D::Error> { d.read_seq(|d, len| { let mut v = Vec::with_capacity(len); for i in 0..len { @@ -591,13 +591,8 @@ where [T]: ToOwned>, { fn decode(d: &mut D) -> Result, D::Error> { - d.read_seq(|d, len| { - let mut v = Vec::with_capacity(len); - for i in 0..len { - v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(Cow::Owned(v)) - }) + let v: Vec = Decodable::decode(d)?; + Ok(Cow::Owned(v)) } } diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs index b0f7e785b7..a2bcf2c251 100644 --- a/compiler/rustc_serialize/tests/leb128.rs +++ b/compiler/rustc_serialize/tests/leb128.rs @@ -1,18 +1,36 @@ +#![feature(int_bits_const)] +#![feature(maybe_uninit_slice)] +#![feature(maybe_uninit_uninit_array)] + use rustc_serialize::leb128::*; +use std::mem::MaybeUninit; macro_rules! impl_test_unsigned_leb128 { ($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => { #[test] fn $test_name() { + // Test 256 evenly spaced values of integer range, + // integer max value, and some "random" numbers. + let mut values = Vec::new(); + + let increment = (1 as $int_ty) << ($int_ty::BITS - 8); + values.extend((0..256).map(|i| $int_ty::MIN + i * increment)); + + values.push($int_ty::MAX); + + values.extend( + (-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFu64 as $int_ty)), + ); + let mut stream = Vec::new(); - for x in 0..62 { - $write_fn_name(&mut stream, (3u64 << x) as $int_ty); + for &x in &values { + let mut buf = MaybeUninit::uninit_array(); + stream.extend($write_fn_name(&mut buf, x)); } let mut position = 0; - for x in 0..62 { - let expected = (3u64 << x) as $int_ty; + for &expected in &values { let (actual, bytes_read) = $read_fn_name(&stream[position..]); assert_eq!(expected, actual); position += bytes_read; @@ -28,18 +46,49 @@ impl_test_unsigned_leb128!(test_u64_leb128, write_u64_leb128, read_u64_leb128, u impl_test_unsigned_leb128!(test_u128_leb128, write_u128_leb128, read_u128_leb128, u128); impl_test_unsigned_leb128!(test_usize_leb128, write_usize_leb128, read_usize_leb128, usize); -#[test] -fn test_signed_leb128() { - let values: Vec<_> = (-500..500).map(|i| i * 0x12345789ABCDEF).collect(); - let mut stream = Vec::new(); - for &x in &values { - write_signed_leb128(&mut stream, x); - } - let mut pos = 0; - for &x in &values { - let (value, bytes_read) = read_signed_leb128(&mut stream, pos); - pos += bytes_read; - assert_eq!(x, value); - } - assert_eq!(pos, stream.len()); +macro_rules! impl_test_signed_leb128 { + ($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => { + #[test] + fn $test_name() { + // Test 256 evenly spaced values of integer range, + // integer max value, and some "random" numbers. + let mut values = Vec::new(); + + let mut value = $int_ty::MIN; + let increment = (1 as $int_ty) << ($int_ty::BITS - 8); + + for _ in 0..256 { + values.push(value); + // The addition in the last loop iteration overflows. + value = value.wrapping_add(increment); + } + + values.push($int_ty::MAX); + + values.extend( + (-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFi64 as $int_ty)), + ); + + let mut stream = Vec::new(); + + for &x in &values { + let mut buf = MaybeUninit::uninit_array(); + stream.extend($write_fn_name(&mut buf, x)); + } + + let mut position = 0; + for &expected in &values { + let (actual, bytes_read) = $read_fn_name(&stream[position..]); + assert_eq!(expected, actual); + position += bytes_read; + } + assert_eq!(stream.len(), position); + } + }; } + +impl_test_signed_leb128!(test_i16_leb128, write_i16_leb128, read_i16_leb128, i16); +impl_test_signed_leb128!(test_i32_leb128, write_i32_leb128, read_i32_leb128, i32); +impl_test_signed_leb128!(test_i64_leb128, write_i64_leb128, read_i64_leb128, i64); +impl_test_signed_leb128!(test_i128_leb128, write_i128_leb128, read_i128_leb128, i128); +impl_test_signed_leb128!(test_isize_leb128, write_isize_leb128, read_isize_leb128, isize); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index c9ddcbdb5f..a6d4dcb34c 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -5,7 +5,7 @@ pub use crate::options::*; use crate::lint; use crate::search_paths::SearchPath; -use crate::utils::NativeLibKind; +use crate::utils::{CanonicalizedPath, NativeLibKind}; use crate::{early_error, early_warn, Session}; use rustc_data_structures::fx::FxHashSet; @@ -13,7 +13,7 @@ use rustc_data_structures::impl_stable_hash_via_hash; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_target::abi::{Align, TargetDataLayout}; -use rustc_target::spec::{Target, TargetTriple}; +use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple}; use crate::parse::CrateConfig; use rustc_feature::UnstableFeatures; @@ -221,23 +221,6 @@ pub enum DebugInfo { Full, } -/// Some debuginfo requires link-time relocation and some does not. LLVM can partition the debuginfo -/// into sections depending on whether or not it requires link-time relocation. Split DWARF -/// provides a mechanism which allows the linker to skip the sections which don't require link-time -/// relocation - either by putting those sections into DWARF object files, or keeping them in the -/// object file in such a way that the linker will skip them. -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum SplitDwarfKind { - /// Disabled. - None, - /// Sections which do not require relocation are written into the object file but ignored - /// by the linker. - Single, - /// Sections which do not require relocation are written into a DWARF object (`.dwo`) file, - /// which is skipped by the linker by virtue of being a different file. - Split, -} - #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] #[derive(Encodable, Decodable)] pub enum OutputType { @@ -361,7 +344,7 @@ impl Default for TrimmedDefPaths { /// Use tree-based collections to cheaply get a deterministic `Hash` implementation. /// *Do not* switch `BTreeMap` out for an unsorted container type! That would break /// dependency tracking for command-line arguments. -#[derive(Clone, Hash)] +#[derive(Clone, Hash, Debug)] pub struct OutputTypes(BTreeMap>); impl_stable_hash_via_hash!(OutputTypes); @@ -403,6 +386,20 @@ impl OutputTypes { OutputType::Metadata | OutputType::DepInfo => false, }) } + + // Returns `true` if any of the output types require linking. + pub fn should_link(&self) -> bool { + self.0.keys().any(|k| match *k { + OutputType::Bitcode + | OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::Metadata + | OutputType::Object + | OutputType::DepInfo => false, + OutputType::Exe => true, + }) + } } /// Use tree-based collections to cheaply get a deterministic `Hash` implementation. @@ -439,7 +436,7 @@ pub enum ExternLocation { /// which one to use. /// /// Added via `--extern prelude_name=some_file.rlib` - ExactPaths(BTreeSet), + ExactPaths(BTreeSet), } impl Externs { @@ -461,7 +458,7 @@ impl ExternEntry { ExternEntry { location, is_private_dep: false, add_prelude: false } } - pub fn files(&self) -> Option> { + pub fn files(&self) -> Option> { match &self.location { ExternLocation::ExactPaths(set) => Some(set.iter()), _ => None, @@ -538,7 +535,7 @@ impl Input { } } -#[derive(Clone, Hash)] +#[derive(Clone, Hash, Debug)] pub struct OutputFilenames { pub out_directory: PathBuf, filestem: String, @@ -621,10 +618,10 @@ impl OutputFilenames { /// mode is being used, which is the logic that this function is intended to encapsulate. pub fn split_dwarf_filename( &self, - split_dwarf_kind: SplitDwarfKind, + split_debuginfo_kind: SplitDebuginfo, cgu_name: Option<&str>, ) -> Option { - self.split_dwarf_path(split_dwarf_kind, cgu_name) + self.split_dwarf_path(split_debuginfo_kind, cgu_name) .map(|path| path.strip_prefix(&self.out_directory).unwrap_or(&path).to_path_buf()) } @@ -632,19 +629,19 @@ impl OutputFilenames { /// mode is being used, which is the logic that this function is intended to encapsulate. pub fn split_dwarf_path( &self, - split_dwarf_kind: SplitDwarfKind, + split_debuginfo_kind: SplitDebuginfo, cgu_name: Option<&str>, ) -> Option { let obj_out = self.temp_path(OutputType::Object, cgu_name); let dwo_out = self.temp_path_dwo(cgu_name); - match split_dwarf_kind { - SplitDwarfKind::None => None, + match split_debuginfo_kind { + SplitDebuginfo::Off => None, // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes // (pointing at the path which is being determined here). Use the path to the current // object file. - SplitDwarfKind::Single => Some(obj_out), + SplitDebuginfo::Packed => Some(obj_out), // Split mode emits the DWARF into a different file, use that path. - SplitDwarfKind::Split => Some(dwo_out), + SplitDebuginfo::Unpacked => Some(dwo_out), } } } @@ -819,7 +816,7 @@ pub fn default_configuration(sess: &Session) -> CrateConfig { } } ret.insert((sym::target_arch, Some(Symbol::intern(arch)))); - ret.insert((sym::target_endian, Some(Symbol::intern(end)))); + ret.insert((sym::target_endian, Some(Symbol::intern(end.as_str())))); ret.insert((sym::target_pointer_width, Some(Symbol::intern(&wordsz)))); ret.insert((sym::target_env, Some(Symbol::intern(env)))); ret.insert((sym::target_vendor, Some(Symbol::intern(vendor)))); @@ -1293,7 +1290,7 @@ pub fn parse_error_format( error_format } -fn parse_crate_edition(matches: &getopts::Matches) -> Edition { +pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition { let edition = match matches.opt_str("edition") { Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { early_error( @@ -1308,12 +1305,11 @@ fn parse_crate_edition(matches: &getopts::Matches) -> Edition { None => DEFAULT_EDITION, }; - if !edition.is_stable() && !nightly_options::match_is_nightly_build(matches) { + if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) { early_error( ErrorOutputType::default(), &format!( - "edition {} is unstable and only \ - available for nightly builds of rustc.", + "edition {} is unstable and only available with -Z unstable-options.", edition, ), ) @@ -1643,13 +1639,15 @@ pub fn parse_externs( for arg in matches.opt_strs("extern") { let (name, path) = match arg.split_once('=') { None => (arg, None), - Some((name, path)) => (name.to_string(), Some(path.to_string())), + Some((name, path)) => (name.to_string(), Some(Path::new(path))), }; let (options, name) = match name.split_once(':') { None => (None, name), Some((opts, name)) => (Some(opts), name.to_string()), }; + let path = path.map(|p| CanonicalizedPath::new(p)); + let entry = externs.entry(name.to_owned()); use std::collections::btree_map::Entry; @@ -1830,11 +1828,17 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } if debugging_opts.mir_opt_level > 1 { + // Functions inlined during MIR transform can, at best, make it impossible to + // effectively cover inlined functions, and, at worst, break coverage map generation + // during LLVM codegen. For example, function counter IDs are only unique within a + // function. Inlining after these counters are injected can produce duplicate counters, + // resulting in an invalid coverage map (and ICE); so this option combination is not + // allowed. early_warn( error_format, &format!( - "`-Z mir-opt-level={}` (any level > 1) enables function inlining, which \ - limits the effectiveness of `-Z instrument-coverage`.", + "`-Z mir-opt-level={}` (or any level > 1) enables function inlining, which \ + is incompatible with `-Z instrument-coverage`. Inlining will be disabled.", debugging_opts.mir_opt_level, ), ); @@ -1891,6 +1895,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let pretty = parse_pretty(matches, &debugging_opts, error_format); + if !debugging_opts.unstable_options + && !target_triple.triple().contains("apple") + && cg.split_debuginfo.is_some() + { + { + early_error(error_format, "`-Csplit-debuginfo` is unstable on this platform"); + } + } + Options { crate_types, optimize: opt_level, @@ -2167,11 +2180,12 @@ crate mod dep_tracking { SymbolManglingVersion, TrimmedDefPaths, }; use crate::lint; + use crate::options::WasiExecModel; use crate::utils::NativeLibKind; use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel}; - use rustc_target::spec::{RelroLevel, TargetTriple, TlsModel}; + use rustc_target::spec::{RelroLevel, SplitDebuginfo, TargetTriple, TlsModel}; use std::collections::hash_map::DefaultHasher; use std::collections::BTreeMap; use std::hash::Hash; @@ -2222,6 +2236,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); @@ -2242,6 +2257,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(TargetTriple); impl_dep_tracking_hash_via_hash!(Edition); impl_dep_tracking_hash_via_hash!(LinkerPluginLto); + impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 55ee4e5208..47f14fa6b7 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -76,7 +76,7 @@ impl<'a> FileSearch<'a> { pub fn new( sysroot: &'a Path, triple: &'a str, - search_paths: &'a Vec, + search_paths: &'a [SearchPath], tlib_path: &'a SearchPath, kind: PathKind, ) -> FileSearch<'a> { @@ -113,6 +113,8 @@ pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { sysroot.join(&relative_target_lib_path(sysroot, target_triple)) } +// This function checks if sysroot is found using env::args().next(), and if it +// is not found, uses env::current_exe() to imply sysroot. pub fn get_or_default_sysroot() -> PathBuf { // Follow symlinks. If the resolved path is relative, make it absolute. fn canonicalize(path: PathBuf) -> PathBuf { @@ -123,15 +125,51 @@ pub fn get_or_default_sysroot() -> PathBuf { fix_windows_verbatim_for_gcc(&path) } - match env::current_exe() { - Ok(exe) => { - let mut p = canonicalize(exe); - p.pop(); - p.pop(); - p + // Use env::current_exe() to get the path of the executable following + // symlinks/canonicalizing components. + fn from_current_exe() -> PathBuf { + match env::current_exe() { + Ok(exe) => { + let mut p = canonicalize(exe); + p.pop(); + p.pop(); + p + } + Err(e) => panic!("failed to get current_exe: {}", e), + } + } + + // Use env::args().next() to get the path of the executable without + // following symlinks/canonicalizing any component. This makes the rustc + // binary able to locate Rust libraries in systems using content-addressable + // storage (CAS). + fn from_env_args_next() -> Option { + match env::args_os().next() { + Some(first_arg) => { + let mut p = PathBuf::from(first_arg); + + // Check if sysroot is found using env::args().next() only if the rustc in argv[0] + // is a symlink (see #79253). We might want to change/remove it to conform with + // https://www.gnu.org/prep/standards/standards.html#Finding-Program-Files in the + // future. + if fs::read_link(&p).is_err() { + // Path is not a symbolic link or does not exist. + return None; + } + + p.pop(); + p.pop(); + let mut libdir = PathBuf::from(&p); + libdir.push(find_libdir(&p).as_ref()); + if libdir.exists() { Some(p) } else { None } + } + None => None, } - Err(e) => panic!("failed to get current_exe: {}", e), } + + // Check if sysroot is found using env::args().next(), and if is not found, + // use env::current_exe() to imply sysroot. + from_env_args_next().unwrap_or(from_current_exe()) } // The name of the directory rustc expects libraries to be located. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 81f79f4b0e..779e042163 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -6,7 +6,7 @@ use crate::search_paths::SearchPath; use crate::utils::NativeLibKind; use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy}; -use rustc_target::spec::{RelocModel, RelroLevel, TargetTriple, TlsModel}; +use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TargetTriple, TlsModel}; use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; @@ -269,7 +269,6 @@ macro_rules! options { pub const parse_switch_with_opt_path: &str = "an optional path to the profiling data output directory"; pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`"; - pub const parse_split_dwarf_kind: &str = "one of: `none`, `single` or `split`"; pub const parse_symbol_mangling_version: &str = "either `legacy` or `v0` (RFC 2603)"; pub const parse_src_file_hash: &str = "either `md5` or `sha1`"; pub const parse_relocation_model: &str = @@ -279,6 +278,9 @@ macro_rules! options { pub const parse_tls_model: &str = "one of supported TLS models (`rustc --print tls-models`)"; pub const parse_target_feature: &str = parse_string; + pub const parse_wasi_exec_model: &str = "either `command` or `reactor`"; + pub const parse_split_debuginfo: &str = + "one of supported split-debuginfo modes (`off` or `dsymutil`)"; } #[allow(dead_code)] @@ -677,19 +679,6 @@ macro_rules! options { true } - fn parse_split_dwarf_kind( - slot: &mut SplitDwarfKind, - v: Option<&str>, - ) -> bool { - *slot = match v { - Some("none") => SplitDwarfKind::None, - Some("split") => SplitDwarfKind::Split, - Some("single") => SplitDwarfKind::Single, - _ => return false, - }; - true - } - fn parse_symbol_mangling_version( slot: &mut Option, v: Option<&str>, @@ -722,6 +711,23 @@ macro_rules! options { None => false, } } + + fn parse_wasi_exec_model(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some("command") => *slot = Some(WasiExecModel::Command), + Some("reactor") => *slot = Some(WasiExecModel::Reactor), + _ => return false, + } + true + } + + fn parse_split_debuginfo(slot: &mut Option, v: Option<&str>) -> bool { + match v.and_then(|s| SplitDebuginfo::from_str(s).ok()) { + Some(e) => *slot = Some(e), + _ => return false, + } + true + } } ) } @@ -820,6 +826,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "save all temporary output files during compilation (default: no)"), soft_float: bool = (false, parse_bool, [TRACKED], "use soft float ABI (*eabihf targets only) (default: no)"), + split_debuginfo: Option = (None, parse_split_debuginfo, [TRACKED], + "how to handle split-debuginfo, a platform-specific option"), target_cpu: Option = (None, parse_opt_string, [TRACKED], "select target processor (`rustc --print target-cpus` for details)"), target_feature: String = (String::new(), parse_target_feature, [TRACKED], @@ -846,6 +854,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "only allow the listed language features to be enabled in code (space separated)"), always_encode_mir: bool = (false, parse_bool, [TRACKED], "encode MIR of all functions into the crate metadata (default: no)"), + assume_incomplete_release: bool = (false, parse_bool, [TRACKED], + "make cfg(version) treat the current version as incomplete (default: no)"), asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior) (default: no)"), ast_json: bool = (false, parse_bool, [UNTRACKED], @@ -1063,11 +1073,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "choose which RELRO level to use"), report_delayed_bugs: bool = (false, parse_bool, [TRACKED], "immediately print bugs registered with `delay_span_bug` (default: no)"), - // The default historical behavior was to always run dsymutil, so we're - // preserving that temporarily, but we're likely to switch the default - // soon. - run_dsymutil: bool = (true, parse_bool, [TRACKED], - "if on Mac, run `dsymutil` and delete intermediate object files (default: yes)"), sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], "use a sanitizer"), sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], @@ -1102,8 +1107,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), - split_dwarf: SplitDwarfKind = (SplitDwarfKind::None, parse_split_dwarf_kind, [UNTRACKED], - "enable generation of split dwarf"), split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED], "provide minimal debug info in the object/executable to facilitate online \ symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), @@ -1166,9 +1169,17 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "in general, enable more debug printouts (default: no)"), verify_llvm_ir: bool = (false, parse_bool, [TRACKED], "verify LLVM IR (default: no)"), + wasi_exec_model: Option = (None, parse_wasi_exec_model, [TRACKED], + "whether to build a wasi command or reactor"), // This list is in alphabetical order. // // If you add a new option, please update: - // - src/librustc_interface/tests.rs + // - compiler/rustc_interface/src/tests.rs +} + +#[derive(Clone, Hash)] +pub enum WasiExecModel { + Command, + Reactor, } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index b1a4834241..81b3834741 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -138,6 +138,8 @@ pub struct ParseSess { pub env_depinfo: Lock)>>, /// All the type ascriptions expressions that have had a suggestion for likely path typo. pub type_ascription_path_suggestions: Lock>, + /// Whether cfg(version) should treat the current release as incomplete + pub assume_incomplete_release: bool, } impl ParseSess { @@ -164,6 +166,7 @@ impl ParseSess { reached_eof: Lock::new(false), env_depinfo: Default::default(), type_ascription_path_suggestions: Default::default(), + assume_incomplete_release: false, } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 75faab12e3..69aa72d899 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -28,7 +28,7 @@ use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, S use rustc_span::{sym, SourceFileHashAlgorithm, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel}; -use rustc_target::spec::{Target, TargetTriple, TlsModel}; +use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple, TlsModel}; use std::cell::{self, RefCell}; use std::env; @@ -796,6 +796,22 @@ impl Session { self.opts.debugging_opts.tls_model.unwrap_or(self.target.tls_model) } + pub fn is_wasi_reactor(&self) -> bool { + self.target.options.os == "wasi" + && matches!( + self.opts.debugging_opts.wasi_exec_model, + Some(config::WasiExecModel::Reactor) + ) + } + + pub fn split_debuginfo(&self) -> SplitDebuginfo { + self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) + } + + pub fn target_can_use_split_dwarf(&self) -> bool { + !self.target.is_like_windows && !self.target.is_like_osx + } + pub fn must_not_eliminate_frame_pointers(&self) -> bool { // "mcount" function relies on stack pointer. // See . @@ -1076,6 +1092,11 @@ impl Session { self.opts.edition >= Edition::Edition2018 } + /// Are we allowed to use features from the Rust 2021 edition? + pub fn rust_2021(&self) -> bool { + self.opts.edition >= Edition::Edition2021 + } + pub fn edition(&self) -> Edition { self.opts.edition } @@ -1323,7 +1344,8 @@ pub fn build_session( None }; - let parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map); + let mut parse_sess = ParseSess::with_span_handler(span_diagnostic, source_map); + parse_sess.assume_incomplete_release = sopts.debugging_opts.assume_incomplete_release; let sysroot = match &sopts.maybe_sysroot { Some(sysroot) => sysroot.clone(), None => filesearch::get_or_default_sysroot(), @@ -1345,7 +1367,7 @@ pub fn build_session( let optimization_fuel_crate = sopts.debugging_opts.fuel.as_ref().map(|i| i.0.clone()); let optimization_fuel = Lock::new(OptimizationFuel { - remaining: sopts.debugging_opts.fuel.as_ref().map(|i| i.1).unwrap_or(0), + remaining: sopts.debugging_opts.fuel.as_ref().map_or(0, |i| i.1), out_of_fuel: false, }); let print_fuel_crate = sopts.debugging_opts.print_fuel.clone(); @@ -1517,6 +1539,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } const ASAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-apple-darwin", "aarch64-fuchsia", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", @@ -1524,11 +1547,16 @@ fn validate_commandline_args_with_session_available(sess: &Session) { "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu", ]; - const LSAN_SUPPORTED_TARGETS: &[&str] = - &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + const LSAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", + ]; const MSAN_SUPPORTED_TARGETS: &[&str] = &["aarch64-unknown-linux-gnu", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu"]; const TSAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-freebsd", diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 15447c01d1..f3d3330912 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,5 +1,6 @@ use crate::session::Session; use rustc_data_structures::profiling::VerboseTimingGuard; +use std::path::{Path, PathBuf}; impl Session { pub fn timer<'a>(&'a self, what: &'static str) -> VerboseTimingGuard<'a> { @@ -30,3 +31,25 @@ pub enum NativeLibKind { } rustc_data_structures::impl_stable_hash_via_hash!(NativeLibKind); + +/// A path that has been canonicalized along with its original, non-canonicalized form +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct CanonicalizedPath { + // Optional since canonicalization can sometimes fail + canonicalized: Option, + original: PathBuf, +} + +impl CanonicalizedPath { + pub fn new(path: &Path) -> Self { + Self { original: path.to_owned(), canonicalized: std::fs::canonicalize(path).ok() } + } + + pub fn canonicalized(&self) -> &PathBuf { + self.canonicalized.as_ref().unwrap_or(self.original()) + } + + pub fn original(&self) -> &PathBuf { + &self.original + } +} diff --git a/compiler/rustc_span/src/analyze_source_file/tests.rs b/compiler/rustc_span/src/analyze_source_file/tests.rs index cb418a4bda..66aefc9a78 100644 --- a/compiler/rustc_span/src/analyze_source_file/tests.rs +++ b/compiler/rustc_span/src/analyze_source_file/tests.rs @@ -12,7 +12,7 @@ macro_rules! test { let (lines, multi_byte_chars, non_narrow_chars) = analyze_source_file($text, BytePos($source_file_start_pos)); - let expected_lines: Vec = $lines.into_iter().map(|pos| BytePos(pos)).collect(); + let expected_lines: Vec = $lines.into_iter().map(BytePos).collect(); assert_eq!(lines, expected_lines); diff --git a/compiler/rustc_span/src/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs index 15dd00fb48..8e21b9ff44 100644 --- a/compiler/rustc_span/src/caching_source_map_view.rs +++ b/compiler/rustc_span/src/caching_source_map_view.rs @@ -1,5 +1,5 @@ use crate::source_map::SourceMap; -use crate::{BytePos, SourceFile}; +use crate::{BytePos, SourceFile, SpanData}; use rustc_data_structures::sync::Lrc; use std::ops::Range; @@ -24,6 +24,32 @@ struct CacheEntry { file_index: usize, } +impl CacheEntry { + #[inline] + fn update( + &mut self, + new_file_and_idx: Option<(Lrc, usize)>, + pos: BytePos, + time_stamp: usize, + ) { + if let Some((file, file_idx)) = new_file_and_idx { + self.file = file; + self.file_index = file_idx; + } + + let line_index = self.file.lookup_line(pos).unwrap(); + let line_bounds = self.file.line_bounds(line_index); + self.line_number = line_index + 1; + self.line = line_bounds; + self.touch(time_stamp); + } + + #[inline] + fn touch(&mut self, time_stamp: usize) { + self.time_stamp = time_stamp; + } +} + #[derive(Clone)] pub struct CachingSourceMapView<'sm> { source_map: &'sm SourceMap, @@ -57,59 +83,202 @@ impl<'sm> CachingSourceMapView<'sm> { self.time_stamp += 1; // Check if the position is in one of the cached lines - for cache_entry in self.line_cache.iter_mut() { - if cache_entry.line.contains(&pos) { - cache_entry.time_stamp = self.time_stamp; + let cache_idx = self.cache_entry_index(pos); + if cache_idx != -1 { + let cache_entry = &mut self.line_cache[cache_idx as usize]; + cache_entry.touch(self.time_stamp); - return Some(( - cache_entry.file.clone(), - cache_entry.line_number, - pos - cache_entry.line.start, - )); - } + return Some(( + cache_entry.file.clone(), + cache_entry.line_number, + pos - cache_entry.line.start, + )); } // No cache hit ... - let mut oldest = 0; - for index in 1..self.line_cache.len() { - if self.line_cache[index].time_stamp < self.line_cache[oldest].time_stamp { - oldest = index; - } - } + let oldest = self.oldest_cache_entry_index(); + + // If the entry doesn't point to the correct file, get the new file and index. + let new_file_and_idx = if !file_contains(&self.line_cache[oldest].file, pos) { + Some(self.file_for_position(pos)?) + } else { + None + }; let cache_entry = &mut self.line_cache[oldest]; + cache_entry.update(new_file_and_idx, pos, self.time_stamp); - // If the entry doesn't point to the correct file, fix it up - if !file_contains(&cache_entry.file, pos) { - let file_valid; - if self.source_map.files().len() > 0 { - let file_index = self.source_map.lookup_source_file_idx(pos); - let file = self.source_map.files()[file_index].clone(); - - if file_contains(&file, pos) { - cache_entry.file = file; - cache_entry.file_index = file_index; - file_valid = true; - } else { - file_valid = false; + Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line.start)) + } + + pub fn span_data_to_lines_and_cols( + &mut self, + span_data: &SpanData, + ) -> Option<(Lrc, usize, BytePos, usize, BytePos)> { + self.time_stamp += 1; + + // Check if lo and hi are in the cached lines. + let lo_cache_idx = self.cache_entry_index(span_data.lo); + let hi_cache_idx = self.cache_entry_index(span_data.hi); + + if lo_cache_idx != -1 && hi_cache_idx != -1 { + // Cache hit for span lo and hi. Check if they belong to the same file. + let result = { + let lo = &self.line_cache[lo_cache_idx as usize]; + let hi = &self.line_cache[hi_cache_idx as usize]; + + if lo.file_index != hi.file_index { + return None; } - } else { - file_valid = false; + + ( + lo.file.clone(), + lo.line_number, + span_data.lo - lo.line.start, + hi.line_number, + span_data.hi - hi.line.start, + ) + }; + + self.line_cache[lo_cache_idx as usize].touch(self.time_stamp); + self.line_cache[hi_cache_idx as usize].touch(self.time_stamp); + + return Some(result); + } + + // No cache hit or cache hit for only one of span lo and hi. + let oldest = if lo_cache_idx != -1 || hi_cache_idx != -1 { + let avoid_idx = if lo_cache_idx != -1 { lo_cache_idx } else { hi_cache_idx }; + self.oldest_cache_entry_index_avoid(avoid_idx as usize) + } else { + self.oldest_cache_entry_index() + }; + + // If the entry doesn't point to the correct file, get the new file and index. + // Return early if the file containing beginning of span doesn't contain end of span. + let new_file_and_idx = if !file_contains(&self.line_cache[oldest].file, span_data.lo) { + let new_file_and_idx = self.file_for_position(span_data.lo)?; + if !file_contains(&new_file_and_idx.0, span_data.hi) { + return None; } - if !file_valid { + Some(new_file_and_idx) + } else { + let file = &self.line_cache[oldest].file; + if !file_contains(&file, span_data.hi) { return None; } + + None + }; + + // Update the cache entries. + let (lo_idx, hi_idx) = match (lo_cache_idx, hi_cache_idx) { + // Oldest cache entry is for span_data.lo line. + (-1, -1) => { + let lo = &mut self.line_cache[oldest]; + lo.update(new_file_and_idx, span_data.lo, self.time_stamp); + + if !lo.line.contains(&span_data.hi) { + let new_file_and_idx = Some((lo.file.clone(), lo.file_index)); + let next_oldest = self.oldest_cache_entry_index_avoid(oldest); + let hi = &mut self.line_cache[next_oldest]; + hi.update(new_file_and_idx, span_data.hi, self.time_stamp); + (oldest, next_oldest) + } else { + (oldest, oldest) + } + } + // Oldest cache entry is for span_data.lo line. + (-1, _) => { + let lo = &mut self.line_cache[oldest]; + lo.update(new_file_and_idx, span_data.lo, self.time_stamp); + let hi = &mut self.line_cache[hi_cache_idx as usize]; + hi.touch(self.time_stamp); + (oldest, hi_cache_idx as usize) + } + // Oldest cache entry is for span_data.hi line. + (_, -1) => { + let hi = &mut self.line_cache[oldest]; + hi.update(new_file_and_idx, span_data.hi, self.time_stamp); + let lo = &mut self.line_cache[lo_cache_idx as usize]; + lo.touch(self.time_stamp); + (lo_cache_idx as usize, oldest) + } + _ => { + panic!(); + } + }; + + let lo = &self.line_cache[lo_idx]; + let hi = &self.line_cache[hi_idx]; + + // Span lo and hi may equal line end when last line doesn't + // end in newline, hence the inclusive upper bounds below. + debug_assert!(span_data.lo >= lo.line.start); + debug_assert!(span_data.lo <= lo.line.end); + debug_assert!(span_data.hi >= hi.line.start); + debug_assert!(span_data.hi <= hi.line.end); + debug_assert!(lo.file.contains(span_data.lo)); + debug_assert!(lo.file.contains(span_data.hi)); + debug_assert_eq!(lo.file_index, hi.file_index); + + Some(( + lo.file.clone(), + lo.line_number, + span_data.lo - lo.line.start, + hi.line_number, + span_data.hi - hi.line.start, + )) + } + + fn cache_entry_index(&self, pos: BytePos) -> isize { + for (idx, cache_entry) in self.line_cache.iter().enumerate() { + if cache_entry.line.contains(&pos) { + return idx as isize; + } + } + + -1 + } + + fn oldest_cache_entry_index(&self) -> usize { + let mut oldest = 0; + + for idx in 1..self.line_cache.len() { + if self.line_cache[idx].time_stamp < self.line_cache[oldest].time_stamp { + oldest = idx; + } } - let line_index = cache_entry.file.lookup_line(pos).unwrap(); - let line_bounds = cache_entry.file.line_bounds(line_index); + oldest + } - cache_entry.line_number = line_index + 1; - cache_entry.line = line_bounds; - cache_entry.time_stamp = self.time_stamp; + fn oldest_cache_entry_index_avoid(&self, avoid_idx: usize) -> usize { + let mut oldest = if avoid_idx != 0 { 0 } else { 1 }; - Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line.start)) + for idx in 0..self.line_cache.len() { + if idx != avoid_idx + && self.line_cache[idx].time_stamp < self.line_cache[oldest].time_stamp + { + oldest = idx; + } + } + + oldest + } + + fn file_for_position(&self, pos: BytePos) -> Option<(Lrc, usize)> { + if !self.source_map.files().is_empty() { + let file_idx = self.source_map.lookup_source_file_idx(pos); + let file = &self.source_map.files()[file_idx]; + + if file_contains(file, pos) { + return Some((file.clone(), file_idx)); + } + } + + None } } diff --git a/compiler/rustc_span/src/edition.rs b/compiler/rustc_span/src/edition.rs index 4d0c92f51d..a9200dd7df 100644 --- a/compiler/rustc_span/src/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -4,35 +4,42 @@ use std::str::FromStr; use rustc_macros::HashStable_Generic; -/// The edition of the compiler (RFC 2052) +/// The edition of the compiler. (See [RFC 2052](https://github.com/rust-lang/rfcs/blob/master/text/2052-epochs.md).) #[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, Encodable, Decodable, Eq)] #[derive(HashStable_Generic)] pub enum Edition { - // editions must be kept in order, oldest to newest + // When adding new editions, be sure to do the following: + // + // - update the `ALL_EDITIONS` const + // - update the `EDITION_NAME_LIST` const + // - add a `rust_####()` function to the session + // - update the enum in Cargo's sources as well + // + // Editions *must* be kept in order, oldest to newest. /// The 2015 edition Edition2015, /// The 2018 edition Edition2018, - // when adding new editions, be sure to update: - // - // - Update the `ALL_EDITIONS` const - // - Update the EDITION_NAME_LIST const - // - add a `rust_####()` function to the session - // - update the enum in Cargo's sources as well + /// The 2021 ediiton + Edition2021, } -// must be in order from oldest to newest -pub const ALL_EDITIONS: &[Edition] = &[Edition::Edition2015, Edition::Edition2018]; +// Must be in order from oldest to newest. +pub const ALL_EDITIONS: &[Edition] = + &[Edition::Edition2015, Edition::Edition2018, Edition::Edition2021]; -pub const EDITION_NAME_LIST: &str = "2015|2018"; +pub const EDITION_NAME_LIST: &str = "2015|2018|2021"; pub const DEFAULT_EDITION: Edition = Edition::Edition2015; +pub const LATEST_STABLE_EDITION: Edition = Edition::Edition2018; + impl fmt::Display for Edition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match *self { Edition::Edition2015 => "2015", Edition::Edition2018 => "2018", + Edition::Edition2021 => "2021", }; write!(f, "{}", s) } @@ -43,6 +50,7 @@ impl Edition { match *self { Edition::Edition2015 => "rust_2015_compatibility", Edition::Edition2018 => "rust_2018_compatibility", + Edition::Edition2021 => "rust_2021_compatibility", } } @@ -50,6 +58,7 @@ impl Edition { match *self { Edition::Edition2015 => sym::rust_2015_preview, Edition::Edition2018 => sym::rust_2018_preview, + Edition::Edition2021 => sym::rust_2021_preview, } } @@ -57,6 +66,7 @@ impl Edition { match *self { Edition::Edition2015 => true, Edition::Edition2018 => true, + Edition::Edition2021 => false, } } } @@ -67,6 +77,7 @@ impl FromStr for Edition { match s { "2015" => Ok(Edition::Edition2015), "2018" => Ok(Edition::Edition2018), + "2021" => Ok(Edition::Edition2021), _ => Err(()), } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 0f82db1d05..9f265f37f3 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -27,14 +27,18 @@ use crate::edition::Edition; use crate::symbol::{kw, sym, Symbol}; use crate::SESSION_GLOBALS; -use crate::{Span, DUMMY_SP}; +use crate::{BytePos, CachingSourceMapView, ExpnIdCache, SourceFile, Span, DUMMY_SP}; use crate::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_macros::HashStable_Generic; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use std::fmt; +use std::hash::Hash; +use std::thread::LocalKey; use tracing::*; /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". @@ -80,7 +84,12 @@ pub enum Transparency { impl ExpnId { pub fn fresh(expn_data: Option) -> Self { - HygieneData::with(|data| data.fresh_expn(expn_data)) + let has_data = expn_data.is_some(); + let expn_id = HygieneData::with(|data| data.fresh_expn(expn_data)); + if has_data { + update_disambiguator(expn_id); + } + expn_id } /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. @@ -111,7 +120,8 @@ impl ExpnId { assert!(old_expn_data.is_none(), "expansion data is reset for an expansion ID"); expn_data.orig_id.replace(self.as_u32()).expect_none("orig_id should be None"); *old_expn_data = Some(expn_data); - }) + }); + update_disambiguator(self) } pub fn is_descendant_of(self, ancestor: ExpnId) -> bool { @@ -152,6 +162,12 @@ pub struct HygieneData { expn_data: Vec>, syntax_context_data: Vec, syntax_context_map: FxHashMap<(SyntaxContext, ExpnId, Transparency), SyntaxContext>, + /// Maps the `Fingerprint` of an `ExpnData` to the next disambiguator value. + /// This is used by `update_disambiguator` to keep track of which `ExpnData`s + /// would have collisions without a disambiguator. + /// The keys of this map are always computed with `ExpnData.disambiguator` + /// set to 0. + expn_data_disambiguators: FxHashMap, } impl HygieneData { @@ -175,6 +191,7 @@ impl HygieneData { dollar_crate_name: kw::DollarCrate, }], syntax_context_map: FxHashMap::default(), + expn_data_disambiguators: FxHashMap::default(), } } @@ -622,6 +639,10 @@ impl SyntaxContext { pub fn dollar_crate_name(self) -> Symbol { HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) } + + pub fn edition(self) -> Edition { + self.outer_expn_data().edition + } } impl fmt::Debug for SyntaxContext { @@ -645,11 +666,25 @@ impl Span { expn_data: ExpnData, transparency: Transparency, ) -> Span { + let expn_id = ExpnId::fresh(Some(expn_data)); HygieneData::with(|data| { - let expn_id = data.fresh_expn(Some(expn_data)); self.with_ctxt(data.apply_mark(SyntaxContext::root(), expn_id, transparency)) }) } + + /// Reuses the span but adds information like the kind of the desugaring and features that are + /// allowed inside this span. + pub fn mark_with_reason( + self, + allow_internal_unstable: Option>, + reason: DesugaringKind, + edition: Edition, + ) -> Span { + self.fresh_expansion(ExpnData { + allow_internal_unstable, + ..ExpnData::default(ExpnKind::Desugaring(reason), self, edition, None) + }) + } } /// A subset of properties from both macro definition and macro call available through global data. @@ -699,7 +734,7 @@ pub struct ExpnData { /// created locally - when our serialized metadata is decoded, /// foreign `ExpnId`s will have their `ExpnData` looked up /// from the crate specified by `Crate - pub krate: CrateNum, + krate: CrateNum, /// The raw that this `ExpnData` had in its original crate. /// An `ExpnData` can be created before being assigned an `ExpnId`, /// so this might be `None` until `set_expn_data` is called @@ -707,13 +742,53 @@ pub struct ExpnData { // two `ExpnData`s that differ only in their `orig_id` should // be considered equivalent. #[stable_hasher(ignore)] - pub orig_id: Option, + orig_id: Option, + + /// Used to force two `ExpnData`s to have different `Fingerprint`s. + /// Due to macro expansion, it's possible to end up with two `ExpnId`s + /// that have identical `ExpnData`s. This violates the constract of `HashStable` + /// - the two `ExpnId`s are not equal, but their `Fingerprint`s are equal + /// (since the numerical `ExpnId` value is not considered by the `HashStable` + /// implementation). + /// + /// The `disambiguator` field is set by `update_disambiguator` when two distinct + /// `ExpnId`s would end up with the same `Fingerprint`. Since `ExpnData` includes + /// a `krate` field, this value only needs to be unique within a single crate. + disambiguator: u32, } -// This would require special handling of `orig_id` and `parent` +// These would require special handling of `orig_id`. impl !PartialEq for ExpnData {} +impl !Hash for ExpnData {} impl ExpnData { + pub fn new( + kind: ExpnKind, + parent: ExpnId, + call_site: Span, + def_site: Span, + allow_internal_unstable: Option>, + allow_internal_unsafe: bool, + local_inner_macros: bool, + edition: Edition, + macro_def_id: Option, + ) -> ExpnData { + ExpnData { + kind, + parent, + call_site, + def_site, + allow_internal_unstable, + allow_internal_unsafe, + local_inner_macros, + edition, + macro_def_id, + krate: LOCAL_CRATE, + orig_id: None, + disambiguator: 0, + } + } + /// Constructs expansion data with default properties. pub fn default( kind: ExpnKind, @@ -733,6 +808,7 @@ impl ExpnData { macro_def_id, krate: LOCAL_CRATE, orig_id: None, + disambiguator: 0, } } @@ -1065,7 +1141,7 @@ pub fn decode_syntax_context< parent: SyntaxContext::root(), opaque: SyntaxContext::root(), opaque_and_semitransparent: SyntaxContext::root(), - dollar_crate_name: kw::Invalid, + dollar_crate_name: kw::Empty, }); let mut ctxts = outer_ctxts.lock(); let new_len = raw_id as usize + 1; @@ -1092,7 +1168,7 @@ pub fn decode_syntax_context< ctxt_data, ); // Make sure nothing weird happening while `decode_data` was running - assert_eq!(dummy.dollar_crate_name, kw::Invalid); + assert_eq!(dummy.dollar_crate_name, kw::Empty); }); Ok(new_ctxt) @@ -1232,3 +1308,118 @@ impl Decodable for SyntaxContext { panic!("cannot decode `SyntaxContext` with `{}`", std::any::type_name::()); } } + +/// Updates the `disambiguator` field of the corresponding `ExpnData` +/// such that the `Fingerprint` of the `ExpnData` does not collide with +/// any other `ExpnIds`. +/// +/// This method is called only when an `ExpnData` is first associated +/// with an `ExpnId` (when the `ExpnId` is initially constructed, or via +/// `set_expn_data`). It is *not* called for foreign `ExpnId`s deserialized +/// from another crate's metadata - since `ExpnData` includes a `krate` field, +/// collisions are only possible between `ExpnId`s within the same crate. +fn update_disambiguator(expn_id: ExpnId) { + /// A `HashStableContext` which hashes the raw id values for `DefId` + /// and `CrateNum`, rather than using their computed stable hash. + /// + /// This allows us to use the `HashStable` implementation on `ExpnId` + /// early on in compilation, before we've constructed a `TyCtxt`. + /// The `Fingerprint`s created by this context are not 'stable', since + /// the raw `CrateNum` and `DefId` values for an item may change between + /// sessions due to unrelated changes (e.g. adding/removing an different item). + /// + /// However, this is fine for our purposes - we only need to detect + /// when two `ExpnData`s have the same `Fingerprint`. Since the hashes produced + /// by this context still obey the properties of `HashStable`, we have + /// that + /// `hash_stable(expn1, DummyHashStableContext) == hash_stable(expn2, DummyHashStableContext)` + /// iff `hash_stable(expn1, StableHashingContext) == hash_stable(expn2, StableHasingContext)`. + /// + /// This is sufficient for determining when we need to update the disambiguator. + struct DummyHashStableContext<'a> { + caching_source_map: CachingSourceMapView<'a>, + } + + impl<'a> crate::HashStableContext for DummyHashStableContext<'a> { + fn hash_def_id(&mut self, def_id: DefId, hasher: &mut StableHasher) { + def_id.krate.as_u32().hash_stable(self, hasher); + def_id.index.as_u32().hash_stable(self, hasher); + } + + fn expn_id_cache() -> &'static LocalKey { + // This cache is only used by `DummyHashStableContext`, + // so we won't pollute the cache values of the normal `StableHashingContext` + thread_local! { + static CACHE: ExpnIdCache = Default::default(); + } + + &CACHE + } + + fn hash_crate_num(&mut self, krate: CrateNum, hasher: &mut StableHasher) { + krate.as_u32().hash_stable(self, hasher); + } + fn hash_spans(&self) -> bool { + true + } + fn byte_pos_to_line_and_col( + &mut self, + byte: BytePos, + ) -> Option<(Lrc, usize, BytePos)> { + self.caching_source_map.byte_pos_to_line_and_col(byte) + } + fn span_data_to_lines_and_cols( + &mut self, + span: &crate::SpanData, + ) -> Option<(Lrc, usize, BytePos, usize, BytePos)> { + self.caching_source_map.span_data_to_lines_and_cols(span) + } + } + + let source_map = SESSION_GLOBALS + .with(|session_globals| session_globals.source_map.borrow().as_ref().unwrap().clone()); + + let mut ctx = + DummyHashStableContext { caching_source_map: CachingSourceMapView::new(&source_map) }; + + let mut hasher = StableHasher::new(); + + let expn_data = expn_id.expn_data(); + // This disambiguator should not have been set yet. + assert_eq!( + expn_data.disambiguator, 0, + "Already set disambiguator for ExpnData: {:?}", + expn_data + ); + expn_data.hash_stable(&mut ctx, &mut hasher); + let first_hash = hasher.finish(); + + let modified = HygieneData::with(|data| { + // If this is the first ExpnData with a given hash, then keep our + // disambiguator at 0 (the default u32 value) + let disambig = data.expn_data_disambiguators.entry(first_hash).or_default(); + data.expn_data[expn_id.0 as usize].as_mut().unwrap().disambiguator = *disambig; + *disambig += 1; + + *disambig != 1 + }); + + if modified { + info!("Set disambiguator for {:?} (hash {:?})", expn_id, first_hash); + info!("expn_data = {:?}", expn_id.expn_data()); + + // Verify that the new disambiguator makes the hash unique + #[cfg(debug_assertions)] + { + hasher = StableHasher::new(); + expn_id.expn_data().hash_stable(&mut ctx, &mut hasher); + let new_hash: Fingerprint = hasher.finish(); + + HygieneData::with(|data| { + data.expn_data_disambiguators + .get(&new_hash) + .expect_none("Hash collision after disambiguator update!"); + }); + }; + } +} diff --git a/compiler/rustc_span/src/lev_distance.rs b/compiler/rustc_span/src/lev_distance.rs index edc6625a6e..cea7871923 100644 --- a/compiler/rustc_span/src/lev_distance.rs +++ b/compiler/rustc_span/src/lev_distance.rs @@ -1,10 +1,16 @@ +//! Levenshtein distances. +//! +//! The [Levenshtein distance] is a metric for measuring the difference between two strings. +//! +//! [Levenshtein distance]: https://en.wikipedia.org/wiki/Levenshtein_distance + use crate::symbol::Symbol; use std::cmp; #[cfg(test)] mod tests; -/// Finds the Levenshtein distance between two strings +/// Finds the Levenshtein distance between two strings. pub fn lev_distance(a: &str, b: &str) -> usize { // cases which don't require further computation if a.is_empty() { @@ -35,14 +41,14 @@ pub fn lev_distance(a: &str, b: &str) -> usize { dcol[t_last + 1] } -/// Finds the best match for a given word in the given iterator +/// Finds the best match for a given word in the given iterator. /// /// As a loose rule to avoid the obviously incorrect suggestions, it takes /// an optional limit for the maximum allowable edit distance, which defaults /// to one-third of the given word. /// -/// Besides Levenshtein, we use case insensitive comparison to improve accuracy on an edge case with -/// a lower(upper)case letters mismatch. +/// Besides Levenshtein, we use case insensitive comparison to improve accuracy +/// on an edge case with a lower(upper)case letters mismatch. #[cold] pub fn find_best_match_for_name( name_vec: &[Symbol], @@ -98,7 +104,7 @@ fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option String { let mut split_words: Vec<&str> = name.split('_').collect(); - // We are sorting primitive &strs and can use unstable sort here + // We are sorting primitive &strs and can use unstable sort here. split_words.sort_unstable(); split_words.join("_") } diff --git a/compiler/rustc_span/src/lev_distance/tests.rs b/compiler/rustc_span/src/lev_distance/tests.rs index 7aa01cb8ef..90e20afc8f 100644 --- a/compiler/rustc_span/src/lev_distance/tests.rs +++ b/compiler/rustc_span/src/lev_distance/tests.rs @@ -4,7 +4,7 @@ use super::*; fn test_lev_distance() { use std::char::{from_u32, MAX}; // Test bytelength agnosticity - for c in (0..MAX as u32).filter_map(|i| from_u32(i)).map(|i| i.to_string()) { + for c in (0..MAX as u32).filter_map(from_u32).map(|i| i.to_string()) { assert_eq!(lev_distance(&c[..], &c[..]), 0); } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index fbef4d0670..f3d876a577 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1,4 +1,13 @@ -//! The source positions and related helper functions. +//! Source positions and related helper functions. +//! +//! Important concepts in this module include: +//! +//! - the *span*, represented by [`SpanData`] and related types; +//! - source code as represented by a [`SourceMap`]; and +//! - interned strings, represented by [`Symbol`]s, with some common symbols available statically in the [`sym`] module. +//! +//! Unlike most compilers, the span contains not only the position in the source code, but also various other metadata, +//! such as the edition and macro hygiene. This metadata is stored in [`SyntaxContext`] and [`ExpnData`]. //! //! ## Note //! @@ -56,6 +65,7 @@ use std::hash::Hash; use std::ops::{Add, Range, Sub}; use std::path::{Path, PathBuf}; use std::str::FromStr; +use std::thread::LocalKey; use md5::Md5; use sha1::Digest; @@ -124,7 +134,7 @@ pub enum RealFileName { impl RealFileName { /// Returns the path suitable for reading from the file system on the local host. - /// Avoid embedding this in build artifacts; see `stable_name` for that. + /// Avoid embedding this in build artifacts; see `stable_name()` for that. pub fn local_path(&self) -> &Path { match self { RealFileName::Named(p) @@ -133,7 +143,7 @@ impl RealFileName { } /// Returns the path suitable for reading from the file system on the local host. - /// Avoid embedding this in build artifacts; see `stable_name` for that. + /// Avoid embedding this in build artifacts; see `stable_name()` for that. pub fn into_local_path(self) -> PathBuf { match self { RealFileName::Named(p) @@ -143,7 +153,7 @@ impl RealFileName { /// Returns the path suitable for embedding into build artifacts. Note that /// a virtualized path will not correspond to a valid file system path; see - /// `local_path` for something that is more likely to return paths into the + /// `local_path()` for something that is more likely to return paths into the /// local host file system. pub fn stable_name(&self) -> &Path { match self { @@ -173,7 +183,7 @@ pub enum FileName { /// Custom sources for explicit parser calls from plugins and drivers. Custom(String), DocTest(PathBuf, isize), - /// Post-substitution inline assembly from LLVM + /// Post-substitution inline assembly from LLVM. InlineAsm(u64), } @@ -266,14 +276,17 @@ impl FileName { } } +/// Represents a span. +/// /// Spans represent a region of code, used for error reporting. Positions in spans -/// are *absolute* positions from the beginning of the source_map, not positions -/// relative to `SourceFile`s. Methods on the `SourceMap` can be used to relate spans back +/// are *absolute* positions from the beginning of the [`SourceMap`], not positions +/// relative to [`SourceFile`]s. Methods on the `SourceMap` can be used to relate spans back /// to the original source. -/// You must be careful if the span crosses more than one file - you will not be +/// +/// You must be careful if the span crosses more than one file, since you will not be /// able to use many of the functions on spans in source_map and you cannot assume -/// that the length of the `span = hi - lo`; there may be space in the `BytePos` -/// range between files. +/// that the length of the span is equal to `span.hi - span.lo`; there may be space in the +/// [`BytePos`] range between files. /// /// `SpanData` is public because `Span` uses a thread-local interner and can't be /// sent to other threads, but some pieces of performance infra run in a separate thread. @@ -288,6 +301,10 @@ pub struct SpanData { } impl SpanData { + #[inline] + pub fn span(&self) -> Span { + Span::new(self.lo, self.hi, self.ctxt) + } #[inline] pub fn with_lo(&self, lo: BytePos) -> Span { Span::new(lo, self.hi, self.ctxt) @@ -384,7 +401,7 @@ impl Span { Span::new(lo, hi, SyntaxContext::root()) } - /// Returns a new span representing an empty span at the beginning of this span + /// Returns a new span representing an empty span at the beginning of this span. #[inline] pub fn shrink_to_lo(self) -> Span { let span = self.data(); @@ -398,7 +415,7 @@ impl Span { } #[inline] - /// Returns true if hi == lo + /// Returns `true` if `hi == lo`. pub fn is_empty(&self) -> bool { let span = self.data(); span.hi == span.lo @@ -456,7 +473,7 @@ impl Span { /// Edition of the crate from which this span came. pub fn edition(self) -> edition::Edition { - self.ctxt().outer_expn_data().edition + self.ctxt().edition() } #[inline] @@ -469,6 +486,11 @@ impl Span { self.edition() >= edition::Edition::Edition2018 } + #[inline] + pub fn rust_2021(&self) -> bool { + self.edition() >= edition::Edition::Edition2021 + } + /// Returns the source callee. /// /// Returns `None` if the supplied span has no expansion trace, @@ -512,7 +534,7 @@ impl Span { } /// Checks if a span is "internal" to a macro in which `unsafe` - /// can be used without triggering the `unsafe_code` lint + /// can be used without triggering the `unsafe_code` lint. // (that is, a macro marked with `#[allow_internal_unsafe]`). pub fn allows_unsafe(&self) -> bool { self.ctxt().outer_expn_data().allow_internal_unsafe @@ -700,6 +722,7 @@ impl Span { } } +/// A span together with some additional data. #[derive(Clone, Debug)] pub struct SpanLabel { /// The span we are going to include in the final snippet. @@ -743,7 +766,7 @@ impl Decodable for Span { /// any spans that are debug-printed during the closure's execution. /// /// Normally, the global `TyCtxt` is used to retrieve the `SourceMap` -/// (see `rustc_interface::callbacks::span_debug1). However, some parts +/// (see `rustc_interface::callbacks::span_debug1`). However, some parts /// of the compiler (e.g. `rustc_parse`) may debug-print `Span`s before /// a `TyCtxt` is available. In this case, we fall back to /// the `SourceMap` provided to this function. If that is not available, @@ -994,9 +1017,9 @@ pub enum ExternalSource { Unneeded, Foreign { kind: ExternalSourceKind, - /// This SourceFile's byte-offset within the source_map of its original crate + /// This SourceFile's byte-offset within the source_map of its original crate. original_start_pos: BytePos, - /// The end of this SourceFile within the source_map of its original crate + /// The end of this SourceFile within the source_map of its original crate. original_end_pos: BytePos, }, } @@ -1099,7 +1122,7 @@ impl SourceFileHash { } } -/// A single source in the `SourceMap`. +/// A single source in the [`SourceMap`]. #[derive(Clone)] pub struct SourceFile { /// The name of the file that the source came from. Source that doesn't @@ -1580,7 +1603,7 @@ fn remove_bom(src: &mut String, normalized_pos: &mut Vec) { /// Replaces `\r\n` with `\n` in-place in `src`. /// -/// Returns error if there's a lone `\r` in the string +/// Returns error if there's a lone `\r` in the string. fn normalize_newlines(src: &mut String, normalized_pos: &mut Vec) { if !src.as_bytes().contains(&b'\r') { return; @@ -1705,13 +1728,16 @@ macro_rules! impl_pos { } impl_pos! { - /// A byte offset. Keep this small (currently 32-bits), as AST contains - /// a lot of them. + /// A byte offset. + /// + /// Keep this small (currently 32-bits), as AST contains a lot of them. #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] pub struct BytePos(pub u32); - /// A character offset. Because of multibyte UTF-8 characters, a byte offset - /// is not equivalent to a character offset. The `SourceMap` will convert `BytePos` + /// A character offset. + /// + /// Because of multibyte UTF-8 characters, a byte offset + /// is not equivalent to a character offset. The [`SourceMap`] will convert [`BytePos`] /// values to `CharPos` values as necessary. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct CharPos(pub usize); @@ -1835,16 +1861,26 @@ fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize { } /// Requirements for a `StableHashingContext` to be used in this crate. -/// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc_middle. +/// +/// This is a hack to allow using the [`HashStable_Generic`] derive macro +/// instead of implementing everything in rustc_middle. pub trait HashStableContext { fn hash_def_id(&mut self, _: DefId, hasher: &mut StableHasher); + /// Obtains a cache for storing the `Fingerprint` of an `ExpnId`. + /// This method allows us to have multiple `HashStableContext` implementations + /// that hash things in a different way, without the results of one polluting + /// the cache of the other. + fn expn_id_cache() -> &'static LocalKey; fn hash_crate_num(&mut self, _: CrateNum, hasher: &mut StableHasher); fn hash_spans(&self) -> bool; fn byte_pos_to_line_and_col( &mut self, byte: BytePos, ) -> Option<(Lrc, usize, BytePos)>; + fn span_data_to_lines_and_cols( + &mut self, + span: &SpanData, + ) -> Option<(Lrc, usize, BytePos, usize, BytePos)>; } impl HashStable for Span @@ -1856,6 +1892,7 @@ where /// offsets into the `SourceMap`). Instead, we hash the (file name, line, column) /// triple, which stays the same even if the containing `SourceFile` has moved /// within the `SourceMap`. + /// /// Also note that we are hashing byte offsets for the column, not unicode /// codepoint offsets. For the purpose of the hash that's sufficient. /// Also, hashing filenames is expensive so we avoid doing it twice when the @@ -1868,8 +1905,9 @@ where return; } - if *self == DUMMY_SP { + if self.is_dummy() { Hash::hash(&TAG_INVALID_SPAN, hasher); + self.ctxt().hash_stable(ctx, hasher); return; } @@ -1877,22 +1915,8 @@ where // position that belongs to it, as opposed to hashing the first // position past it. let span = self.data(); - let (file_lo, line_lo, col_lo) = match ctx.byte_pos_to_line_and_col(span.lo) { - Some(pos) => pos, - None => { - Hash::hash(&TAG_INVALID_SPAN, hasher); - span.ctxt.hash_stable(ctx, hasher); - return; - } - }; - - if !file_lo.contains(span.hi) { - Hash::hash(&TAG_INVALID_SPAN, hasher); - span.ctxt.hash_stable(ctx, hasher); - return; - } - - let (_, line_hi, col_hi) = match ctx.byte_pos_to_line_and_col(span.hi) { + let (file, line_lo, col_lo, line_hi, col_hi) = match ctx.span_data_to_lines_and_cols(&span) + { Some(pos) => pos, None => { Hash::hash(&TAG_INVALID_SPAN, hasher); @@ -1904,7 +1928,7 @@ where Hash::hash(&TAG_VALID_SPAN, hasher); // We truncate the stable ID hash and line and column numbers. The chances // of causing a collision this way should be minimal. - Hash::hash(&(file_lo.name_hash as u64), hasher); + Hash::hash(&(file.name_hash as u64), hasher); // Hash both the length and the end location (line/column) of a span. If we // hash only the length, for example, then two otherwise equal spans with @@ -1943,15 +1967,10 @@ impl HashStable for SyntaxContext { } } +pub type ExpnIdCache = RefCell>>; + impl HashStable for ExpnId { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - // Since the same expansion context is usually referenced many - // times, we cache a stable hash of it and hash that instead of - // recursing every time. - thread_local! { - static CACHE: RefCell>> = Default::default(); - } - const TAG_ROOT: u8 = 0; const TAG_NOT_ROOT: u8 = 1; @@ -1960,8 +1979,11 @@ impl HashStable for ExpnId { return; } + // Since the same expansion context is usually referenced many + // times, we cache a stable hash of it and hash that instead of + // recursing every time. let index = self.as_u32() as usize; - let res = CACHE.with(|cache| cache.borrow().get(index).copied().flatten()); + let res = CTX::expn_id_cache().with(|cache| cache.borrow().get(index).copied().flatten()); if let Some(res) = res { res.hash_stable(ctx, hasher); @@ -1973,7 +1995,7 @@ impl HashStable for ExpnId { self.expn_data().hash_stable(ctx, &mut sub_hasher); let sub_hash: Fingerprint = sub_hasher.finish(); - CACHE.with(|cache| { + CTX::expn_id_cache().with(|cache| { let mut cache = cache.borrow_mut(); if cache.len() < new_len { cache.resize(new_len, None); diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index e9b4eb6e4a..2b429372dc 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -1,9 +1,11 @@ -//! The `SourceMap` tracks all the source code used within a single crate, mapping +//! Types for tracking pieces of source code within a crate. +//! +//! The [`SourceMap`] tracks all the source code used within a single crate, mapping //! from integer byte positions to the original source code location. Each bit //! of source parsed during crate parsing (typically files, in-memory strings, //! or various bits of macro expansion) cover a continuous range of bytes in the -//! `SourceMap` and are represented by `SourceFile`s. Byte positions are stored in -//! `Span` and used pervasively in the compiler. They are absolute positions +//! `SourceMap` and are represented by [`SourceFile`]s. Byte positions are stored in +//! [`Span`] and used pervasively in the compiler. They are absolute positions //! within the `SourceMap`, which upon request can be converted to line and column //! information, source code snippets, etc. @@ -537,7 +539,7 @@ impl SourceMap { pub fn is_line_before_span_empty(&self, sp: Span) -> bool { match self.span_to_prev_source(sp) { - Ok(s) => s.split('\n').last().map(|l| l.trim_start().is_empty()).unwrap_or(false), + Ok(s) => s.split('\n').last().map_or(false, |l| l.trim_start().is_empty()), Err(_) => false, } } @@ -566,7 +568,7 @@ impl SourceMap { // asserting that the line numbers here are all indeed 1-based. let hi_line = hi.line.saturating_sub(1); for line_index in lo.line.saturating_sub(1)..hi_line { - let line_len = lo.file.get_line(line_index).map(|s| s.chars().count()).unwrap_or(0); + let line_len = lo.file.get_line(line_index).map_or(0, |s| s.chars().count()); lines.push(LineInfo { line_index, start_col, end_col: CharPos::from_usize(line_len) }); start_col = CharPos::from_usize(0); } @@ -580,9 +582,9 @@ impl SourceMap { /// Extracts the source surrounding the given `Span` using the `extract_source` function. The /// extract function takes three arguments: a string slice containing the source, an index in /// the slice for the beginning of the span and an index in the slice for the end of the span. - fn span_to_source(&self, sp: Span, extract_source: F) -> Result + fn span_to_source(&self, sp: Span, extract_source: F) -> Result where - F: Fn(&str, usize, usize) -> Result, + F: Fn(&str, usize, usize) -> Result, { let local_begin = self.lookup_byte_offset(sp.lo()); let local_end = self.lookup_byte_offset(sp.hi()); @@ -646,10 +648,10 @@ impl SourceMap { /// Extends the given `Span` to just after the previous occurrence of `c`. Return the same span /// if no character could be found or if an error occurred while retrieving the code snippet. - pub fn span_extend_to_prev_char(&self, sp: Span, c: char) -> Span { + pub fn span_extend_to_prev_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span { if let Ok(prev_source) = self.span_to_prev_source(sp) { - let prev_source = prev_source.rsplit(c).next().unwrap_or("").trim_start(); - if !prev_source.is_empty() && !prev_source.contains('\n') { + let prev_source = prev_source.rsplit(c).next().unwrap_or(""); + if !prev_source.is_empty() && (!prev_source.contains('\n') || accept_newlines) { return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32)); } } @@ -669,7 +671,9 @@ impl SourceMap { let pat = pat.to_owned() + ws; if let Ok(prev_source) = self.span_to_prev_source(sp) { let prev_source = prev_source.rsplit(&pat).next().unwrap_or("").trim_start(); - if !prev_source.is_empty() && (!prev_source.contains('\n') || accept_newlines) { + if prev_source.is_empty() && sp.lo().0 != 0 { + return sp.with_lo(BytePos(sp.lo().0 - 1)); + } else if !prev_source.contains('\n') || accept_newlines { return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32)); } } @@ -678,6 +682,25 @@ impl SourceMap { sp } + /// Returns the source snippet as `String` after the given `Span`. + pub fn span_to_next_source(&self, sp: Span) -> Result { + self.span_to_source(sp, |src, _, end_index| { + src.get(end_index..).map(|s| s.to_string()).ok_or(SpanSnippetError::IllFormedSpan(sp)) + }) + } + + /// Extends the given `Span` to just after the next occurrence of `c`. + pub fn span_extend_to_next_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span { + if let Ok(next_source) = self.span_to_next_source(sp) { + let next_source = next_source.split(c).next().unwrap_or(""); + if !next_source.is_empty() && (!next_source.contains('\n') || accept_newlines) { + return sp.with_hi(BytePos(sp.hi().0 + next_source.len() as u32)); + } + } + + sp + } + /// Given a `Span`, tries to get a shorter span ending before the first occurrence of `char` /// `c`. pub fn span_until_char(&self, sp: Span, c: char) -> Span { @@ -776,6 +799,9 @@ impl SourceMap { /// Returns a new span representing the next character after the end-point of this span. pub fn next_point(&self, sp: Span) -> Span { + if sp.is_dummy() { + return sp; + } let start_of_next_point = sp.hi().0; let width = self.find_width_of_character_at_span(sp.shrink_to_hi(), true); @@ -868,8 +894,10 @@ impl SourceMap { } pub fn get_source_file(&self, filename: &FileName) -> Option> { + // Remap filename before lookup + let filename = self.path_mapping().map_filename_prefix(filename).0; for sf in self.files.borrow().source_files.iter() { - if *filename == sf.name { + if filename == sf.name { return Some(sf.clone()); } } @@ -1037,4 +1065,15 @@ impl FilePathMapping { (path, false) } + + fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) { + match file { + FileName::Real(realfile) => { + let path = realfile.local_path(); + let (path, mapped) = self.map_prefix(path.to_path_buf()); + (FileName::Real(RealFileName::Named(path)), mapped) + } + other => (other.clone(), false), + } + } } diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index b8459eee4e..3f22829b04 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -107,7 +107,7 @@ fn t7() { fn span_from_selection(input: &str, selection: &str) -> Span { assert_eq!(input.len(), selection.len()); let left_index = selection.find('~').unwrap() as u32; - let right_index = selection.rfind('~').map(|x| x as u32).unwrap_or(left_index); + let right_index = selection.rfind('~').map_or(left_index, |x| x as u32); Span::with_root_ctxt(BytePos(left_index), BytePos(right_index + 1)) } diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs index b05e01d666..ceb9b59b13 100644 --- a/compiler/rustc_span/src/span_encoding.rs +++ b/compiler/rustc_span/src/span_encoding.rs @@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxIndexSet; /// A compressed span. /// -/// `SpanData` is 12 bytes, which is a bit too big to stick everywhere. `Span` +/// Whereas [`SpanData`] is 12 bytes, which is a bit too big to stick everywhere, `Span` /// is a form that only takes up 8 bytes, with less space for the length and /// context. The vast majority (99.9%+) of `SpanData` instances will fit within /// those 8 bytes; any `SpanData` whose fields don't fit into a `Span` are @@ -42,13 +42,11 @@ use rustc_data_structures::fx::FxIndexSet; /// - `base` is 32 bits in both `Span` and `SpanData`, which means that `base` /// values never cause interning. The number of bits needed for `base` /// depends on the crate size. 32 bits allows up to 4 GiB of code in a crate. -/// `script-servo` is the largest crate in `rustc-perf`, requiring 26 bits -/// for some spans. /// - `len` is 15 bits in `Span` (a u16, minus 1 bit for the tag) and 32 bits /// in `SpanData`, which means that large `len` values will cause interning. /// The number of bits needed for `len` does not depend on the crate size. -/// The most common number of bits for `len` are 0--7, with a peak usually at -/// 3 or 4, and then it drops off quickly from 8 onwards. 15 bits is enough +/// The most common numbers of bits for `len` are from 0 to 7, with a peak usually +/// at 3 or 4, and then it drops off quickly from 8 onwards. 15 bits is enough /// for 99.99%+ of cases, but larger values (sometimes 20+ bits) might occur /// dozens of times in a typical crate. /// - `ctxt` is 16 bits in `Span` and 32 bits in `SpanData`, which means that diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 99a523c3f3..86f8061a24 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -25,7 +25,7 @@ symbols! { Keywords { // Special reserved identifiers used internally for elided lifetimes, // unnamed method parameters, crate root module, error recovery etc. - Invalid: "", + Empty: "", PathRoot: "{{root}}", DollarCrate: "$crate", Underscore: "_", @@ -133,6 +133,8 @@ symbols! { Copy, Count, Debug, + DebugStruct, + DebugTuple, Decodable, Decoder, Default, @@ -218,6 +220,7 @@ symbols! { abi, abi_amdgpu_kernel, abi_avr_interrupt, + abi_c_cmse_nonsecure_call, abi_efiapi, abi_msp430_interrupt, abi_ptx, @@ -368,6 +371,7 @@ symbols! { const_fn_transmute, const_fn_union, const_generics, + const_generics_defaults, const_if_match, const_impl_trait, const_in_array_repeat_expressions, @@ -380,6 +384,7 @@ symbols! { const_ptr, const_raw_ptr_deref, const_raw_ptr_to_usize_cast, + const_refs_to_cell, const_slice_ptr, const_trait_bound_opt_out, const_trait_impl, @@ -396,6 +401,8 @@ symbols! { copysignf64, core, core_intrinsics, + core_panic, + core_panic_2015_macro, core_panic_macro, cosf32, cosf64, @@ -470,6 +477,7 @@ symbols! { dropck_parametricity, dylib, dyn_trait, + edition_macro_pats, eh_catch_typeinfo, eh_personality, emit_enum, @@ -619,6 +627,8 @@ symbols! { intel, into_iter, into_result, + into_trait, + intra_doc_pointers, intrinsics, irrefutable_let_patterns, isa_attribute, @@ -713,7 +723,6 @@ symbols! { more_struct_aliases, movbe_target_feature, move_ref_pattern, - move_val_init, mul, mul_assign, mul_with_overflow, @@ -791,6 +800,8 @@ symbols! { owned_box, packed, panic, + panic_2015, + panic_2021, panic_abort, panic_bounds_check, panic_handler, @@ -808,6 +819,8 @@ symbols! { partial_ord, passes, pat, + pat2018, + pat2021, path, pattern_parentheses, phantom_data, @@ -894,6 +907,7 @@ symbols! { register_attr, register_tool, relaxed_adts, + relaxed_struct_unsize, rem, rem_assign, repr, @@ -920,6 +934,7 @@ symbols! { rust, rust_2015_preview, rust_2018_preview, + rust_2021_preview, rust_begin_unwind, rust_eh_catch_typeinfo, rust_eh_personality, @@ -1089,6 +1104,8 @@ symbols! { staticlib, std, std_inject, + std_panic, + std_panic_2015_macro, std_panic_macro, stmt, stmt_expr_attributes, @@ -1153,6 +1170,8 @@ symbols! { truncf32, truncf64, try_blocks, + try_from_trait, + try_into_trait, try_trait, tt, tuple, @@ -1273,7 +1292,7 @@ impl Ident { #[inline] pub fn invalid() -> Ident { - Ident::with_dummy_span(kw::Invalid) + Ident::with_dummy_span(kw::Empty) } /// Maps a string to an identifier with a dummy span. @@ -1451,12 +1470,6 @@ impl Symbol { with_interner(|interner| interner.intern(string)) } - /// Access the symbol's chars. This is a slowish operation because it - /// requires locking the symbol interner. - pub fn with R, R>(self, f: F) -> R { - with_interner(|interner| f(interner.get(self))) - } - /// Convert to a `SymbolStr`. This is a slowish operation because it /// requires locking the symbol interner. pub fn as_str(self) -> SymbolStr { @@ -1470,7 +1483,7 @@ impl Symbol { } pub fn is_empty(self) -> bool { - self == kw::Invalid + self == kw::Empty } /// This method is supposed to be used in error messages, so it's expected to be @@ -1484,19 +1497,19 @@ impl Symbol { impl fmt::Debug for Symbol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.with(|str| fmt::Debug::fmt(&str, f)) + fmt::Debug::fmt(&self.as_str(), f) } } impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.with(|str| fmt::Display::fmt(&str, f)) + fmt::Display::fmt(&self.as_str(), f) } } impl Encodable for Symbol { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - self.with(|string| s.emit_str(string)) + s.emit_str(&self.as_str()) } } @@ -1654,7 +1667,7 @@ impl Symbol { /// Returns `true` if this symbol can be a raw identifier. pub fn can_be_raw(self) -> bool { - self != kw::Invalid && self != kw::Underscore && !self.is_path_segment_keyword() + self != kw::Empty && self != kw::Underscore && !self.is_path_segment_keyword() } } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 6356a7e783..b0d5f34090 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -56,7 +56,15 @@ pub(super) fn mangle( let hash = get_symbol_hash(tcx, instance, instance_ty, instantiating_crate); let mut printer = SymbolPrinter { tcx, path: SymbolPath::new(), keep_within_component: false } - .print_def_path(def_id, &[]) + .print_def_path( + def_id, + if let ty::InstanceDef::DropGlue(_, _) = instance.def { + // Add the name of the dropped type to the symbol name + &*instance.substs + } else { + &[] + }, + ) .unwrap(); if let ty::InstanceDef::VtableShim(..) = instance.def { diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 7b6e6ad069..bbf7ecc39c 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -1,4 +1,3 @@ -use rustc_ast::{FloatTy, IntTy, UintTy}; use rustc_data_structures::base_n; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -6,7 +5,7 @@ use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; use rustc_middle::ty::print::{Print, Printer}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeFoldable, UintTy}; use rustc_target::spec::abi::Abi; use std::fmt::Write; @@ -531,7 +530,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { if val < 0 { neg = true; } - Some(val.wrapping_abs() as u128) + Some(val.unsigned_abs()) }) } _ => { diff --git a/compiler/rustc_target/src/abi/call/aarch64.rs b/compiler/rustc_target/src/abi/call/aarch64.rs index 1ab7722eda..a5e985d471 100644 --- a/compiler/rustc_target/src/abi/call/aarch64.rs +++ b/compiler/rustc_target/src/abi/call/aarch64.rs @@ -40,17 +40,7 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { - let unit = if bits <= 8 { - Reg::i8() - } else if bits <= 16 { - Reg::i16() - } else if bits <= 32 { - Reg::i32() - } else { - Reg::i64() - }; - - ret.cast_to(Uniform { unit, total: size }); + ret.cast_to(Uniform { unit: Reg::i64(), total: size }); return; } ret.make_indirect(); @@ -72,17 +62,7 @@ where let size = arg.layout.size; let bits = size.bits(); if bits <= 128 { - let unit = if bits <= 8 { - Reg::i8() - } else if bits <= 16 { - Reg::i16() - } else if bits <= 32 { - Reg::i32() - } else { - Reg::i64() - }; - - arg.cast_to(Uniform { unit, total: size }); + arg.cast_to(Uniform { unit: Reg::i64(), total: size }); return; } arg.make_indirect(); diff --git a/compiler/rustc_target/src/abi/call/arm.rs b/compiler/rustc_target/src/abi/call/arm.rs index 26fed3bae4..b560e11fe1 100644 --- a/compiler/rustc_target/src/abi/call/arm.rs +++ b/compiler/rustc_target/src/abi/call/arm.rs @@ -45,14 +45,7 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 32 { - let unit = if bits <= 8 { - Reg::i8() - } else if bits <= 16 { - Reg::i16() - } else { - Reg::i32() - }; - ret.cast_to(Uniform { unit, total: size }); + ret.cast_to(Uniform { unit: Reg::i32(), total: size }); return; } ret.make_indirect(); diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 5de9a8dfa7..9c49922c28 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -27,10 +27,16 @@ mod x86_win64; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum PassMode { /// Ignore the argument. + /// + /// The argument is either uninhabited or a ZST. Ignore, /// Pass the argument directly. + /// + /// The argument has a layout abi of `Scalar`, `Vector` or in rare cases `Aggregate`. Direct(ArgAttributes), /// Pass a pair's elements directly in two arguments. + /// + /// The argument has a layout abi of `ScalarPair`. Pair(ArgAttributes, ArgAttributes), /// Pass the argument after casting it, to either /// a single uniform or a pair of registers. @@ -97,7 +103,12 @@ impl ArgAttributes { } pub fn ext(&mut self, ext: ArgExtension) -> &mut Self { - assert!(self.arg_ext == ArgExtension::None || self.arg_ext == ext); + assert!( + self.arg_ext == ArgExtension::None || self.arg_ext == ext, + "cannot set {:?} when {:?} is already set", + ext, + self.arg_ext + ); self.arg_ext = ext; self } @@ -434,28 +445,49 @@ pub struct ArgAbi<'a, Ty> { } impl<'a, Ty> ArgAbi<'a, Ty> { - pub fn new(layout: TyAndLayout<'a, Ty>) -> Self { - ArgAbi { layout, pad: None, mode: PassMode::Direct(ArgAttributes::new()) } + pub fn new( + cx: &impl HasDataLayout, + layout: TyAndLayout<'a, Ty>, + scalar_attrs: impl Fn(&TyAndLayout<'a, Ty>, &abi::Scalar, Size) -> ArgAttributes, + ) -> Self { + let mode = match &layout.abi { + Abi::Uninhabited => PassMode::Ignore, + Abi::Scalar(scalar) => PassMode::Direct(scalar_attrs(&layout, scalar, Size::ZERO)), + Abi::ScalarPair(a, b) => PassMode::Pair( + scalar_attrs(&layout, a, Size::ZERO), + scalar_attrs(&layout, b, a.value.size(cx).align_to(b.value.align(cx).abi)), + ), + Abi::Vector { .. } => PassMode::Direct(ArgAttributes::new()), + Abi::Aggregate { .. } => PassMode::Direct(ArgAttributes::new()), + }; + ArgAbi { layout, pad: None, mode } } - pub fn make_indirect(&mut self) { - assert_eq!(self.mode, PassMode::Direct(ArgAttributes::new())); - - // Start with fresh attributes for the pointer. + fn indirect_pass_mode(layout: &TyAndLayout<'a, Ty>) -> PassMode { let mut attrs = ArgAttributes::new(); // For non-immediate arguments the callee gets its own copy of // the value on the stack, so there are no aliases. It's also // program-invisible so can't possibly capture attrs.set(ArgAttribute::NoAlias).set(ArgAttribute::NoCapture).set(ArgAttribute::NonNull); - attrs.pointee_size = self.layout.size; + attrs.pointee_size = layout.size; // FIXME(eddyb) We should be doing this, but at least on // i686-pc-windows-msvc, it results in wrong stack offsets. - // attrs.pointee_align = Some(self.layout.align.abi); + // attrs.pointee_align = Some(layout.align.abi); - let extra_attrs = self.layout.is_unsized().then_some(ArgAttributes::new()); + let extra_attrs = layout.is_unsized().then_some(ArgAttributes::new()); - self.mode = PassMode::Indirect { attrs, extra_attrs, on_stack: false }; + PassMode::Indirect { attrs, extra_attrs, on_stack: false } + } + + pub fn make_indirect(&mut self) { + match self.mode { + PassMode::Direct(_) | PassMode::Pair(_, _) => {} + PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: false } => return, + _ => panic!("Tried to make {:?} indirect", self.mode), + } + + self.mode = Self::indirect_pass_mode(&self.layout); } pub fn make_indirect_byval(&mut self) { @@ -486,7 +518,6 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } pub fn cast_to>(&mut self, target: T) { - assert_eq!(self.mode, PassMode::Direct(ArgAttributes::new())); self.mode = PassMode::Cast(target.into()); } @@ -495,7 +526,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } pub fn is_indirect(&self) -> bool { - matches!(self.mode, PassMode::Indirect {..}) + matches!(self.mode, PassMode::Indirect { .. }) } pub fn is_sized_indirect(&self) -> bool { @@ -520,6 +551,7 @@ pub enum Conv { // Target-specific calling conventions. ArmAapcs, + CCmseNonSecureCall, Msp430Intr, @@ -605,10 +637,11 @@ impl<'a, Ty> FnAbi<'a, Ty> { "nvptx64" => nvptx64::compute_abi_info(self), "hexagon" => hexagon::compute_abi_info(self), "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self), - "wasm32" if cx.target_spec().os != "emscripten" => { - wasm32_bindgen_compat::compute_abi_info(self) - } - "wasm32" | "asmjs" => wasm32::compute_abi_info(cx, self), + "wasm32" => match cx.target_spec().os.as_str() { + "emscripten" | "wasi" => wasm32::compute_abi_info(cx, self), + _ => wasm32_bindgen_compat::compute_abi_info(self), + }, + "asmjs" => wasm32::compute_abi_info(cx, self), a => return Err(format!("unrecognized arch \"{}\" in target specification", a)), } diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index a43080b09e..b14b1ef00d 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -4,11 +4,14 @@ pub use Primitive::*; use crate::spec::Target; use std::convert::{TryFrom, TryInto}; +use std::fmt; use std::num::NonZeroUsize; use std::ops::{Add, AddAssign, Deref, Mul, Range, RangeInclusive, Sub}; +use std::str::FromStr; use rustc_index::vec::{Idx, IndexVec}; use rustc_macros::HashStable_Generic; +use rustc_serialize::json::{Json, ToJson}; use rustc_span::Span; pub mod call; @@ -152,22 +155,19 @@ impl TargetDataLayout { } // Perform consistency checks against the Target information. - let endian_str = match dl.endian { - Endian::Little => "little", - Endian::Big => "big", - }; - if endian_str != target.endian { + if dl.endian != target.endian { return Err(format!( "inconsistent target specification: \"data-layout\" claims \ - architecture is {}-endian, while \"target-endian\" is `{}`", - endian_str, target.endian + architecture is {}-endian, while \"target-endian\" is `{}`", + dl.endian.as_str(), + target.endian.as_str(), )); } if dl.pointer_size.bits() != target.pointer_width.into() { return Err(format!( "inconsistent target specification: \"data-layout\" claims \ - pointers are {}-bit, while \"target-pointer-width\" is `{}`", + pointers are {}-bit, while \"target-pointer-width\" is `{}`", dl.pointer_size.bits(), target.pointer_width )); @@ -234,26 +234,75 @@ pub enum Endian { Big, } +impl Endian { + pub fn as_str(&self) -> &'static str { + match self { + Self::Little => "little", + Self::Big => "big", + } + } +} + +impl fmt::Debug for Endian { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl FromStr for Endian { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "little" => Ok(Self::Little), + "big" => Ok(Self::Big), + _ => Err(format!(r#"unknown endian: "{}""#, s)), + } + } +} + +impl ToJson for Endian { + fn to_json(&self) -> Json { + self.as_str().to_json() + } +} + /// Size of a type in bytes. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] #[derive(HashStable_Generic)] pub struct Size { + // The top 3 bits are ALWAYS zero. raw: u64, } impl Size { pub const ZERO: Size = Size { raw: 0 }; - #[inline] + /// Rounds `bits` up to the next-higher byte boundary, if `bits` is + /// is not aligned. pub fn from_bits(bits: impl TryInto) -> Size { let bits = bits.try_into().ok().unwrap(); + + #[cold] + fn overflow(bits: u64) -> ! { + panic!("Size::from_bits({}) has overflowed", bits); + } + + // This is the largest value of `bits` that does not cause overflow + // during rounding, and guarantees that the resulting number of bytes + // cannot cause overflow when multiplied by 8. + if bits > 0xffff_ffff_ffff_fff8 { + overflow(bits); + } + // Avoid potential overflow from `bits + 7`. - Size::from_bytes(bits / 8 + ((bits % 8) + 7) / 8) + Size { raw: bits / 8 + ((bits % 8) + 7) / 8 } } #[inline] pub fn from_bytes(bytes: impl TryInto) -> Size { - Size { raw: bytes.try_into().ok().unwrap() } + let bytes: u64 = bytes.try_into().ok().unwrap(); + Size { raw: bytes } } #[inline] @@ -268,9 +317,7 @@ impl Size { #[inline] pub fn bits(self) -> u64 { - self.bytes().checked_mul(8).unwrap_or_else(|| { - panic!("Size::bits: {} bytes in bits doesn't fit in u64", self.bytes()) - }) + self.raw << 3 } #[inline] @@ -394,16 +441,28 @@ pub struct Align { } impl Align { + #[inline] pub fn from_bits(bits: u64) -> Result { Align::from_bytes(Size::from_bits(bits).bytes()) } + #[inline] pub fn from_bytes(align: u64) -> Result { // Treat an alignment of 0 bytes like 1-byte alignment. if align == 0 { return Ok(Align { pow2: 0 }); } + #[cold] + fn not_power_of_2(align: u64) -> String { + format!("`{}` is not a power of 2", align) + } + + #[cold] + fn too_large(align: u64) -> String { + format!("`{}` is too large", align) + } + let mut bytes = align; let mut pow2: u8 = 0; while (bytes & 1) == 0 { @@ -411,19 +470,21 @@ impl Align { bytes >>= 1; } if bytes != 1 { - return Err(format!("`{}` is not a power of 2", align)); + return Err(not_power_of_2(align)); } if pow2 > 29 { - return Err(format!("`{}` is too large", align)); + return Err(too_large(align)); } Ok(Align { pow2 }) } + #[inline] pub fn bytes(self) -> u64 { 1 << self.pow2 } + #[inline] pub fn bits(self) -> u64 { self.bytes() * 8 } @@ -432,12 +493,14 @@ impl Align { /// (the largest power of two that the offset is a multiple of). /// /// N.B., for an offset of `0`, this happens to return `2^64`. + #[inline] pub fn max_for_offset(offset: Size) -> Align { Align { pow2: offset.bytes().trailing_zeros() as u8 } } /// Lower the alignment, if necessary, such that the given offset /// is aligned to it (the offset is a multiple of the alignment). + #[inline] pub fn restrict_for_offset(self, offset: Size) -> Align { self.min(Align::max_for_offset(offset)) } @@ -619,7 +682,7 @@ pub struct Scalar { impl Scalar { pub fn is_bool(&self) -> bool { - if let Int(I8, _) = self.value { self.valid_range == (0..=1) } else { false } + matches!(self.value, Int(I8, false)) && self.valid_range == (0..=1) } /// Returns the valid range as a `x..y` range. diff --git a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs new file mode 100644 index 0000000000..192c4661c7 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu.rs @@ -0,0 +1,20 @@ +use crate::abi::Endian; +use crate::spec::{Target, TargetOptions}; + +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); + base.max_atomic_width = Some(128); + + Target { + llvm_target: "aarch64_be-unknown-linux-gnu".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 { + unsupported_abis: super::arm_base::unsupported_abis(), + mcount: "\u{1}_mcount".to_string(), + endian: Endian::Big, + ..base + }, + } +} diff --git a/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs new file mode 100644 index 0000000000..5b9e9c9519 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_be_unknown_linux_gnu_ilp32.rs @@ -0,0 +1,20 @@ +use crate::abi::Endian; +use crate::spec::{Target, TargetOptions}; + +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); + base.max_atomic_width = Some(128); + + Target { + llvm_target: "aarch64_be-unknown-linux-gnu_ilp32".to_string(), + pointer_width: 32, + data_layout: "E-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + options: TargetOptions { + unsupported_abis: super::arm_base::unsupported_abis(), + mcount: "\u{1}_mcount".to_string(), + endian: Endian::Big, + ..base + }, + } +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs new file mode 100644 index 0000000000..f2d7576280 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu_ilp32.rs @@ -0,0 +1,18 @@ +use crate::spec::{Target, TargetOptions}; + +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); + base.max_atomic_width = Some(128); + + Target { + llvm_target: "aarch64-unknown-linux-gnu_ilp32".to_string(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + options: TargetOptions { + unsupported_abis: super::arm_base::unsupported_abis(), + mcount: "\u{1}_mcount".to_string(), + ..base + }, + } +} diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs index 1e45739ca2..65e8a4e8db 100644 --- a/compiler/rustc_target/src/spec/abi.rs +++ b/compiler/rustc_target/src/spec/abi.rs @@ -36,6 +36,7 @@ pub enum Abi { EfiApi, AvrInterrupt, AvrNonBlockingInterrupt, + CCmseNonSecureCall, // Multiplatform / generic ABIs System, @@ -81,6 +82,7 @@ const AbiDatas: &[AbiData] = &[ name: "avr-non-blocking-interrupt", generic: false, }, + AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call", generic: false }, // Cross-platform ABIs AbiData { abi: Abi::System, name: "system", generic: true }, AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true }, diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index 8842239521..3b458962b3 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -1,6 +1,6 @@ use std::env; -use crate::spec::{LinkArgs, TargetOptions}; +use crate::spec::{LinkArgs, SplitDebuginfo, TargetOptions}; pub fn opts(os: &str) -> TargetOptions { // ELF TLS is only available in macOS 10.7+. If you try to compile for 10.6 @@ -36,6 +36,10 @@ pub fn opts(os: &str) -> TargetOptions { emit_debug_gdb_scripts: false, eh_frame_header: false, + // The historical default for macOS targets is to run `dsymutil` which + // generates a packed version of debuginfo split from the main file. + split_debuginfo: SplitDebuginfo::Packed, + // This environment variable is pretty magical but is intended for // producing deterministic builds. This was first discovered to be used // by the `ar` tool as a way to control whether or not mtime entries in diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs index c6586b79b8..255740cb9c 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -1,5 +1,6 @@ // Targets the Big endian Cortex-R4/R5 processor (ARMv7-R) +use crate::abi::Endian; use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; use crate::spec::{Target, TargetOptions}; @@ -11,7 +12,7 @@ pub fn target() -> Target { arch: "arm".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs index e3d4397f61..eb82e4d17b 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -1,5 +1,6 @@ // Targets the Cortex-R4F/R5F processor (ARMv7-R) +use crate::abi::Endian; use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; use crate::spec::{Target, TargetOptions}; @@ -11,7 +12,7 @@ pub fn target() -> Target { arch: "arm".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index 76c0bf419e..32da16a2d8 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -5,15 +5,16 @@ //! The `crtx` ones are generally distributed with libc and the `begin/end` ones with gcc. //! See for some more details. //! -//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi | -//! |----------------------|------------------------|------------------------|------------------|-------------------|------| -//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 | -//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | -//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 | -//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | -//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | -//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | -//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - | +//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi | +//! |----------------------|------------------------|------------------------|------------------|-------------------|--------------| +//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 | +//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | +//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | +//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - | +//! | wasi-reactor-exe | N/A | N/A | N/A | N/A | crt1-reactor | //! //! | Post-link CRT objects | glibc | musl | bionic | mingw | wasi | //! |-----------------------|---------------|---------------|----------------|--------|------| @@ -105,6 +106,7 @@ pub(super) fn pre_wasi_fallback() -> CrtObjects { (LinkOutputKind::DynamicPicExe, &["crt1.o"]), (LinkOutputKind::StaticNoPicExe, &["crt1.o"]), (LinkOutputKind::StaticPicExe, &["crt1.o"]), + (LinkOutputKind::WasiReactorExe, &["crt1-reactor.o"]), ]) } diff --git a/compiler/rustc_target/src/spec/i386_apple_ios.rs b/compiler/rustc_target/src/spec/i386_apple_ios.rs index 302306ee57..2f94fbc5ad 100644 --- a/compiler/rustc_target/src/spec/i386_apple_ios.rs +++ b/compiler/rustc_target/src/spec/i386_apple_ios.rs @@ -1,5 +1,5 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{Target, TargetOptions}; +use crate::spec::{StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let base = opts("ios", Arch::I386); @@ -10,6 +10,11 @@ pub fn target() -> Target { f64:32:64-f80:128-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, + options: TargetOptions { + max_atomic_width: Some(64), + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + stack_probes: StackProbeType::Call, + ..base + }, } } diff --git a/compiler/rustc_target/src/spec/i386_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i386_unknown_linux_gnu.rs new file mode 100644 index 0000000000..f329b2d2c8 --- /dev/null +++ b/compiler/rustc_target/src/spec/i386_unknown_linux_gnu.rs @@ -0,0 +1,8 @@ +use crate::spec::Target; + +pub fn target() -> Target { + let mut base = super::i686_unknown_linux_gnu::target(); + base.cpu = "i386".to_string(); + base.llvm_target = "i386-unknown-linux-gnu".to_string(); + base +} diff --git a/compiler/rustc_target/src/spec/i486_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i486_unknown_linux_gnu.rs new file mode 100644 index 0000000000..5d96a558cb --- /dev/null +++ b/compiler/rustc_target/src/spec/i486_unknown_linux_gnu.rs @@ -0,0 +1,8 @@ +use crate::spec::Target; + +pub fn target() -> Target { + let mut base = super::i686_unknown_linux_gnu::target(); + base.cpu = "i486".to_string(); + base.llvm_target = "i486-unknown-linux-gnu".to_string(); + base +} diff --git a/compiler/rustc_target/src/spec/i686_apple_darwin.rs b/compiler/rustc_target/src/spec/i686_apple_darwin.rs index 0ab4034092..31813087c0 100644 --- a/compiler/rustc_target/src/spec/i686_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/i686_apple_darwin.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions}; +use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::apple_base::opts("macos"); @@ -6,7 +6,8 @@ pub fn target() -> Target { base.max_atomic_width = Some(64); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]); base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; base.eliminate_frame_pointer = false; // Clang automatically chooses a more specific target based on diff --git a/compiler/rustc_target/src/spec/i686_linux_android.rs b/compiler/rustc_target/src/spec/i686_linux_android.rs index 52059b930d..89eb41e3cb 100644 --- a/compiler/rustc_target/src/spec/i686_linux_android.rs +++ b/compiler/rustc_target/src/spec/i686_linux_android.rs @@ -1,4 +1,4 @@ -use crate::spec::Target; +use crate::spec::{StackProbeType, Target}; // See https://developer.android.com/ndk/guides/abis.html#x86 // for target ABI requirements. @@ -11,7 +11,8 @@ pub fn target() -> Target { // http://developer.android.com/ndk/guides/abis.html#x86 base.cpu = "pentiumpro".to_string(); base.features = "+mmx,+sse,+sse2,+sse3,+ssse3".to_string(); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "i686-linux-android".to_string(), diff --git a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs index fc1c8607d6..a8cc0997ab 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::freebsd_base::opts(); @@ -7,7 +7,9 @@ pub fn target() -> Target { let pre_link_args = base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); pre_link_args.push("-m32".to_string()); pre_link_args.push("-Wl,-znotext".to_string()); - base.stack_probes = true; + + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "i686-unknown-freebsd".to_string(), diff --git a/compiler/rustc_target/src/spec/i686_unknown_haiku.rs b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs index 22c8ba5475..080791386b 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_haiku.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::haiku_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "i686-unknown-haiku".to_string(), diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs index 083c115d08..656136c497 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "i686-unknown-linux-gnu".to_string(), diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs index 1673b2a180..cb154b798b 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); @@ -6,7 +6,8 @@ pub fn target() -> Target { base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-Wl,-melf_i386".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; // The unwinder used by i686-unknown-linux-musl, the LLVM libunwind // implementation, apparently relies on frame pointers existing... somehow. diff --git a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs index c22139b587..26bdc04fe9 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions}; +use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "i686-unknown-netbsdelf".to_string(), diff --git a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs index 87642efdee..e6a244c925 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::openbsd_base::opts(); @@ -6,7 +6,8 @@ pub fn target() -> Target { base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-fuse-ld=lld".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "i686-unknown-openbsd".to_string(), diff --git a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs index c0825358ca..8732b47823 100644 --- a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "i686-unknown-linux-gnu".to_string(), diff --git a/compiler/rustc_target/src/spec/linux_kernel_base.rs b/compiler/rustc_target/src/spec/linux_kernel_base.rs index a5fc1649e7..ddb9a7b632 100644 --- a/compiler/rustc_target/src/spec/linux_kernel_base.rs +++ b/compiler/rustc_target/src/spec/linux_kernel_base.rs @@ -1,4 +1,6 @@ -use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, TargetOptions}; +use crate::spec::{ + LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, StackProbeType, TargetOptions, +}; pub fn opts() -> TargetOptions { let mut pre_link_args = LinkArgs::new(); @@ -11,7 +13,8 @@ pub fn opts() -> TargetOptions { env: "gnu".to_string(), disable_redzone: true, panic_strategy: PanicStrategy::Abort, - stack_probes: true, + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + stack_probes: StackProbeType::Call, eliminate_frame_pointer: false, linker_is_gnu: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs index daa0d9da17..53398539ac 100644 --- a/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -7,7 +8,7 @@ pub fn target() -> Target { data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), arch: "mips64".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, // NOTE(mips64r2) matches C toolchain cpu: "mips64r2".to_string(), features: "+mips64r2".to_string(), diff --git a/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs index db8d0c04e6..329fbd2272 100644 --- a/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -11,6 +12,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), arch: "mips64".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs index a7ec1f19c9..b41b28cbc8 100644 --- a/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -7,7 +8,7 @@ pub fn target() -> Target { data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, cpu: "mips32r2".to_string(), features: "+mips32r2,+fpxx,+nooddspreg".to_string(), max_atomic_width: Some(32), diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs index 1ebe577bc1..3713af43d7 100644 --- a/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -11,6 +12,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs index 2123d5e1a0..042ec9140f 100644 --- a/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -7,7 +8,7 @@ pub fn target() -> Target { data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, cpu: "mips32r2".to_string(), features: "+mips32r2,+soft-float".to_string(), max_atomic_width: Some(32), diff --git a/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs index 11b3734a10..a81c90fe0c 100644 --- a/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -7,7 +8,7 @@ pub fn target() -> Target { data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, cpu: "mips32r6".to_string(), features: "+mips32r6".to_string(), max_atomic_width: Some(32), diff --git a/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs index 6282c9e1d5..3bf837fbb4 100644 --- a/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -7,7 +8,7 @@ pub fn target() -> Target { data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), arch: "mips64".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, // NOTE(mips64r6) matches C toolchain cpu: "mips64r6".to_string(), features: "+mips64r6".to_string(), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 8d72df6850..7a93bac72c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -34,11 +34,13 @@ //! the target's settings, though `target-feature` and `link-args` will *add* //! to the list specified by the target, rather than replace. +use crate::abi::Endian; use crate::spec::abi::{lookup as lookup_abi, Abi}; use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_serialize::json::{Json, ToJson}; use rustc_span::symbol::{sym, Symbol}; use std::collections::BTreeMap; +use std::convert::TryFrom; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -407,6 +409,8 @@ pub enum LinkOutputKind { DynamicDylib, /// Dynamic library with bundled libc ("statically linked"). StaticDylib, + /// WASI module with a lifetime past the _initialize entry point + WasiReactorExe, } impl LinkOutputKind { @@ -418,6 +422,7 @@ impl LinkOutputKind { LinkOutputKind::StaticPicExe => "static-pic-exe", LinkOutputKind::DynamicDylib => "dynamic-dylib", LinkOutputKind::StaticDylib => "static-dylib", + LinkOutputKind::WasiReactorExe => "wasi-reactor-exe", } } @@ -429,6 +434,7 @@ impl LinkOutputKind { "static-pic-exe" => LinkOutputKind::StaticPicExe, "dynamic-dylib" => LinkOutputKind::DynamicDylib, "static-dylib" => LinkOutputKind::StaticDylib, + "wasi-reactor-exe" => LinkOutputKind::WasiReactorExe, _ => return None, }) } @@ -442,6 +448,69 @@ impl fmt::Display for LinkOutputKind { pub type LinkArgs = BTreeMap>; +#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)] +pub enum SplitDebuginfo { + /// Split debug-information is disabled, meaning that on supported platforms + /// you can find all debug information in the executable itself. This is + /// only supported for ELF effectively. + /// + /// * Windows - not supported + /// * macOS - don't run `dsymutil` + /// * ELF - `.dwarf_*` sections + Off, + + /// Split debug-information can be found in a "packed" location separate + /// from the final artifact. This is supported on all platforms. + /// + /// * Windows - `*.pdb` + /// * macOS - `*.dSYM` (run `dsymutil`) + /// * ELF - `*.dwp` (run `rust-llvm-dwp`) + Packed, + + /// Split debug-information can be found in individual object files on the + /// filesystem. The main executable may point to the object files. + /// + /// * Windows - not supported + /// * macOS - supported, scattered object files + /// * ELF - supported, scattered `*.dwo` files + Unpacked, +} + +impl SplitDebuginfo { + fn as_str(&self) -> &'static str { + match self { + SplitDebuginfo::Off => "off", + SplitDebuginfo::Packed => "packed", + SplitDebuginfo::Unpacked => "unpacked", + } + } +} + +impl FromStr for SplitDebuginfo { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "off" => SplitDebuginfo::Off, + "unpacked" => SplitDebuginfo::Unpacked, + "packed" => SplitDebuginfo::Packed, + _ => return Err(()), + }) + } +} + +impl ToJson for SplitDebuginfo { + fn to_json(&self) -> Json { + self.as_str().to_json() + } +} + +impl fmt::Display for SplitDebuginfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + macro_rules! supported_targets { ( $(($( $triple:literal, )+ $module:ident ),)+ ) => { $(mod $module;)+ @@ -474,6 +543,83 @@ macro_rules! supported_targets { }; } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum StackProbeType { + /// Don't emit any stack probes. + None, + /// It is harmless to use this option even on targets that do not have backend support for + /// stack probes as the failure mode is the same as if no stack-probe option was specified in + /// the first place. + Inline, + /// Call `__rust_probestack` whenever stack needs to be probed. + Call, + /// Use inline option for LLVM versions later than specified in `min_llvm_version_for_inline` + /// and call `__rust_probestack` otherwise. + InlineOrCall { min_llvm_version_for_inline: (u32, u32, u32) }, +} + +impl StackProbeType { + fn from_json(json: &Json) -> Result { + let object = json.as_object().ok_or_else(|| "expected a JSON object")?; + let kind = object + .get("kind") + .and_then(|o| o.as_string()) + .ok_or_else(|| "expected `kind` to be a string")?; + match kind { + "none" => Ok(StackProbeType::None), + "inline" => Ok(StackProbeType::Inline), + "call" => Ok(StackProbeType::Call), + "inline-or-call" => { + let min_version = object + .get("min-llvm-version-for-inline") + .and_then(|o| o.as_array()) + .ok_or_else(|| "expected `min-llvm-version-for-inline` to be an array")?; + let mut iter = min_version.into_iter().map(|v| { + let int = v.as_u64().ok_or_else( + || "expected `min-llvm-version-for-inline` values to be integers", + )?; + u32::try_from(int) + .map_err(|_| "`min-llvm-version-for-inline` values don't convert to u32") + }); + let min_llvm_version_for_inline = ( + iter.next().unwrap_or(Ok(11))?, + iter.next().unwrap_or(Ok(0))?, + iter.next().unwrap_or(Ok(0))?, + ); + Ok(StackProbeType::InlineOrCall { min_llvm_version_for_inline }) + } + _ => Err(String::from( + "`kind` expected to be one of `inline-or-none`, `call` or `inline-or-call`", + )), + } + } +} + +impl ToJson for StackProbeType { + fn to_json(&self) -> Json { + Json::Object(match self { + StackProbeType::None => { + vec![(String::from("kind"), "none".to_json())].into_iter().collect() + } + StackProbeType::Inline => { + vec![(String::from("kind"), "inline".to_json())].into_iter().collect() + } + StackProbeType::Call => { + vec![(String::from("kind"), "call".to_json())].into_iter().collect() + } + StackProbeType::InlineOrCall { min_llvm_version_for_inline } => vec![ + (String::from("kind"), "inline-or-call".to_json()), + ( + String::from("min-llvm-version-for-inline"), + min_llvm_version_for_inline.to_json(), + ), + ] + .into_iter() + .collect(), + }) + } +} + supported_targets! { ("x86_64-unknown-linux-gnu", x86_64_unknown_linux_gnu), ("x86_64-unknown-linux-gnux32", x86_64_unknown_linux_gnux32), @@ -662,6 +808,10 @@ supported_targets! { ("mipsel-sony-psp", mipsel_sony_psp), ("mipsel-unknown-none", mipsel_unknown_none), ("thumbv4t-none-eabi", thumbv4t_none_eabi), + + ("aarch64_be-unknown-linux-gnu", aarch64_be_unknown_linux_gnu), + ("aarch64-unknown-linux-gnu_ilp32", aarch64_unknown_linux_gnu_ilp32), + ("aarch64_be-unknown-linux-gnu_ilp32", aarch64_be_unknown_linux_gnu_ilp32), } /// Everything `rustc` knows about how to compile for a specific target. @@ -705,8 +855,8 @@ pub struct TargetOptions { /// Whether the target is built-in or loaded from a custom target specification. pub is_builtin: bool, - /// String to use as the `target_endian` `cfg` variable. Defaults to "little". - pub endian: String, + /// Used as the `target_endian` `cfg` variable. Defaults to little endian. + pub endian: Endian, /// Width of c_int type. Defaults to "32". pub c_int_width: String, /// OS name to use for conditional compilation (`target_os`). Defaults to "none". @@ -921,8 +1071,8 @@ pub struct TargetOptions { /// Whether or not crt-static is respected by the compiler (or is a no-op). pub crt_static_respected: bool, - /// Whether or not stack probes (__rust_probestack) are enabled - pub stack_probes: bool, + /// The implementation of stack probes to use. + pub stack_probes: StackProbeType, /// The minimum alignment for global symbols. pub min_global_align: Option, @@ -1002,6 +1152,10 @@ pub struct TargetOptions { /// Is true if the target is an ARM architecture using thumb v1 which allows for /// thumb and arm interworking. pub has_thumb_interworking: bool, + + /// How to handle split debug information, if at all. Specifying `None` has + /// target-specific meaning. + pub split_debuginfo: SplitDebuginfo, } impl Default for TargetOptions { @@ -1010,7 +1164,7 @@ impl Default for TargetOptions { fn default() -> TargetOptions { TargetOptions { is_builtin: false, - endian: "little".to_string(), + endian: Endian::Little, c_int_width: "32".to_string(), os: "none".to_string(), env: String::new(), @@ -1080,7 +1234,7 @@ impl Default for TargetOptions { crt_static_allows_dylibs: false, crt_static_default: false, crt_static_respected: false, - stack_probes: false, + stack_probes: StackProbeType::None, min_global_align: None, default_codegen_units: None, trap_unreachable: true, @@ -1101,6 +1255,7 @@ impl Default for TargetOptions { use_ctors_section: false, eh_frame_header: true, has_thumb_interworking: false, + split_debuginfo: SplitDebuginfo::Off, } } } @@ -1299,6 +1454,18 @@ impl Target { Some(Ok(())) })).unwrap_or(Ok(())) } ); + ($key_name:ident, SplitDebuginfo) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::() { + Ok(level) => base.$key_name = level, + _ => return Some(Err(format!("'{}' is not a valid value for \ + split-debuginfo. Use 'off' or 'dsymutil'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); ($key_name:ident, list) => ( { let name = (stringify!($key_name)).replace("_", "-"); if let Some(v) = obj.find(&name).and_then(Json::as_array) { @@ -1356,6 +1523,18 @@ impl Target { Some(Ok(())) })).unwrap_or(Ok(())) } ); + ($key_name:ident, StackProbeType) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| match StackProbeType::from_json(o) { + Ok(v) => { + base.$key_name = v; + Some(Ok(())) + }, + Err(s) => Some(Err( + format!("`{:?}` is not a valid value for `{}`: {}", o, name, s) + )), + }).unwrap_or(Ok(())) + } ); ($key_name:ident, crt_objects_fallback) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { @@ -1377,7 +1556,7 @@ impl Target { let kind = LinkOutputKind::from_str(&k).ok_or_else(|| { format!("{}: '{}' is not a valid value for CRT object kind. \ Use '(dynamic,static)-(nopic,pic)-exe' or \ - '(dynamic,static)-dylib'", name, k) + '(dynamic,static)-dylib' or 'wasi-reactor-exe'", name, k) })?; let v = v.as_array().ok_or_else(|| @@ -1439,8 +1618,10 @@ impl Target { } ); } + if let Some(s) = obj.find("target-endian").and_then(Json::as_string) { + base.endian = s.parse()?; + } key!(is_builtin, bool); - key!(endian = "target-endian"); key!(c_int_width = "target-c-int-width"); key!(os); key!(env); @@ -1509,7 +1690,7 @@ impl Target { key!(crt_static_allows_dylibs, bool); key!(crt_static_default, bool); key!(crt_static_respected, bool); - key!(stack_probes, bool); + key!(stack_probes, StackProbeType)?; key!(min_global_align, Option); key!(default_codegen_units, Option); key!(trap_unreachable, bool); @@ -1530,6 +1711,7 @@ impl Target { key!(use_ctors_section, bool); key!(eh_frame_header, bool); key!(has_thumb_interworking, bool); + key!(split_debuginfo, SplitDebuginfo)?; // NB: The old name is deprecated, but support for it is retained for // compatibility. @@ -1765,6 +1947,7 @@ impl ToJson for Target { target_option_val!(use_ctors_section); target_option_val!(eh_frame_header); target_option_val!(has_thumb_interworking); + target_option_val!(split_debuginfo); if default.unsupported_abis != self.unsupported_abis { d.insert( diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs index 8cd6735a8c..39c0d5f0bb 100644 --- a/compiler/rustc_target/src/spec/msvc_base.rs +++ b/compiler/rustc_target/src/spec/msvc_base.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions}; pub fn opts() -> TargetOptions { let pre_link_args_msvc = vec![ @@ -27,6 +27,10 @@ pub fn opts() -> TargetOptions { abi_return_struct_as_int: true, emit_debug_gdb_scripts: false, + // Currently this is the only supported method of debuginfo on MSVC + // where `*.pdb` files show up next to the final artifact. + split_debuginfo: SplitDebuginfo::Packed, + ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs index 626865aa24..3dddeb1129 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -11,6 +12,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs index 03322818d3..751022c124 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, RelroLevel, Target, TargetOptions}; pub fn target() -> Target { @@ -15,6 +16,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs index 231539756f..546dfbab6c 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -11,6 +12,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs index 1c83e3e64d..bb55872109 100644 --- a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -11,6 +12,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - options: TargetOptions { endian: "big".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs index 3a9271247b..70dd0b2aee 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -10,6 +11,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs index 105a0b21aa..66118b7495 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -10,6 +11,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs index 49d3294478..679a3a2f6a 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -10,6 +11,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs index 387d6cdc45..1245098329 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -10,10 +11,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - options: TargetOptions { - endian: "big".to_string(), - mcount: "__mcount".to_string(), - ..base - }, + options: TargetOptions { endian: Endian::Big, mcount: "__mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs index 20ffa07b99..bb943a8825 100644 --- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -11,10 +12,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - options: TargetOptions { - endian: "big".to_string(), - features: "+secure-plt".to_string(), - ..base - }, + options: TargetOptions { endian: Endian::Big, features: "+secure-plt".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs index 0e713fccd2..4b4f118ba4 100644 --- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -12,7 +13,7 @@ pub fn target() -> Target { data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, // feature msync would disable instruction 'fsync' which is not supported by fsl_p1p2 features: "+secure-plt,+msync".to_string(), ..base diff --git a/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs index d6e8e6ee22..4eeea9bedf 100644 --- a/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs @@ -1,8 +1,9 @@ +use crate::abi::Endian; use crate::spec::Target; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); - base.endian = "big".to_string(); + base.endian = Endian::Big; // z10 is the oldest CPU supported by LLVM base.cpu = "z10".to_string(); // FIXME: The data_layout string below and the ABI implementation in diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs index e9b5520ac3..e1aa48872b 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs @@ -1,8 +1,9 @@ +use crate::abi::Endian; use crate::spec::Target; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); - base.endian = "big".to_string(); + base.endian = Endian::Big; base.cpu = "v9".to_string(); base.max_atomic_width = Some(64); diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs index c8e90f832d..7d685c8310 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -11,10 +12,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), arch: "sparc64".to_string(), - options: TargetOptions { - endian: "big".to_string(), - mcount: "__mcount".to_string(), - ..base - }, + options: TargetOptions { endian: Endian::Big, mcount: "__mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs index 630ce6123f..63b13fad4f 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs @@ -1,8 +1,9 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::openbsd_base::opts(); - base.endian = "big".to_string(); + base.endian = Endian::Big; base.cpu = "v9".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); diff --git a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs index aae186b229..9e8fbff81c 100644 --- a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs @@ -1,8 +1,9 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); - base.endian = "big".to_string(); + base.endian = Endian::Big; base.cpu = "v9".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mv8plus".to_string()); diff --git a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs index 5f99e0b14f..9ac56cae91 100644 --- a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs @@ -1,8 +1,9 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::solaris_base::opts(); - base.endian = "big".to_string(); + base.endian = Endian::Big; base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); // llvm calls this "v9" base.cpu = "v9".to_string(); diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs index 322b6f530e..b9ff16bd19 100644 --- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -9,7 +9,7 @@ // the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all // code runs in the same environment, no process separation is supported. -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, TargetOptions}; pub fn opts() -> TargetOptions { let mut base = super::msvc_base::opts(); @@ -43,7 +43,9 @@ pub fn opts() -> TargetOptions { exe_suffix: ".efi".to_string(), allows_weak_linkage: false, panic_strategy: PanicStrategy::Abort, - stack_probes: true, + // LLVM does not emit inline assembly because the LLVM target does not get considered as… + // "Windows". + stack_probes: StackProbeType::Call, singlethread: true, linker: Some("rust-lld".to_string()), ..base diff --git a/compiler/rustc_target/src/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs index a7957d84cb..bfef3d3722 100644 --- a/compiler/rustc_target/src/spec/wasm32_base.rs +++ b/compiler/rustc_target/src/spec/wasm32_base.rs @@ -55,15 +55,6 @@ pub fn options() -> TargetOptions { // to do so. arg("--no-demangle"); - // The symbol visibility story is a bit in flux right now with LLD. - // It's... not entirely clear to me what's going on, but this looks to - // make everything work when `export_symbols` isn't otherwise called for - // things like executables. - // - // This is really only here to get things working. If it can be removed and - // basic tests still work, then sounds like it should be removed! - arg("--export-dynamic"); - let mut pre_link_args = BTreeMap::new(); pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args); pre_link_args.insert(LinkerFlavor::Gcc, clang_args); diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs index c12757b8f9..9f69ce16c2 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs @@ -2,6 +2,17 @@ use super::wasm32_base; use super::{LinkArgs, LinkerFlavor, PanicStrategy, Target, TargetOptions}; pub fn target() -> Target { + let mut options = wasm32_base::options(); + + let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); + + // Rust really needs a way for users to specify exports and imports in + // the source code. --export-dynamic isn't the right tool for this job, + // however it does have the side effect of automatically exporting a lot + // of symbols, which approximates what people want when compiling for + // wasm32-unknown-unknown expect, so use it for now. + clang_args.push("--export-dynamic".to_string()); + let mut post_link_args = LinkArgs::new(); post_link_args.insert( LinkerFlavor::Em, @@ -28,7 +39,7 @@ pub fn target() -> Target { panic_strategy: PanicStrategy::Unwind, post_link_args, os_family: Some("unix".to_string()), - ..wasm32_base::options() + ..options }; Target { llvm_target: "wasm32-unknown-emscripten".to_string(), diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs index 6037aa5b43..5e89ba2520 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs @@ -26,11 +26,18 @@ pub fn target() -> Target { // For now this target just never has an entry symbol no matter the output // type, so unconditionally pass this. clang_args.push("-Wl,--no-entry".to_string()); - options - .pre_link_args - .get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)) - .unwrap() - .push("--no-entry".to_string()); + + // Rust really needs a way for users to specify exports and imports in + // the source code. --export-dynamic isn't the right tool for this job, + // however it does have the side effect of automatically exporting a lot + // of symbols, which approximates what people want when compiling for + // wasm32-unknown-unknown expect, so use it for now. + clang_args.push("-Wl,--export-dynamic".to_string()); + + // Add the flags to wasm-ld's args too. + let lld_args = options.pre_link_args.get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)).unwrap(); + lld_args.push("--no-entry".to_string()); + lld_args.push("--export-dynamic".to_string()); Target { llvm_target: "wasm32-unknown-unknown".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs index edb33fe6e2..6affd7f1d9 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions}; +use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::apple_base::opts("macos"); @@ -10,7 +10,8 @@ pub fn target() -> Target { vec!["-m64".to_string(), "-arch".to_string(), "x86_64".to_string()], ); base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; // Clang automatically chooses a more specific target based on // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs index c9c7eeb723..ddf68704d4 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs @@ -1,5 +1,5 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{Target, TargetOptions}; +use crate::spec::{StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let base = opts("ios", Arch::X86_64); @@ -9,6 +9,11 @@ pub fn target() -> Target { data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, + options: TargetOptions { + max_atomic_width: Some(64), + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + stack_probes: StackProbeType::Call, + ..base + }, } } diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs index 6b360e5495..e7c3d66b08 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs @@ -1,5 +1,5 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{Target, TargetOptions}; +use crate::spec::{StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let base = opts("ios", Arch::X86_64_macabi); @@ -9,6 +9,11 @@ pub fn target() -> Target { data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, + options: TargetOptions { + max_atomic_width: Some(64), + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + stack_probes: StackProbeType::Call, + ..base + }, } } diff --git a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs index 5b2a62a23f..8727e48136 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs @@ -1,5 +1,5 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{Target, TargetOptions}; +use crate::spec::{StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let base = opts("tvos", Arch::X86_64); @@ -8,6 +8,11 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), - options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, + options: TargetOptions { + max_atomic_width: Some(64), + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + stack_probes: StackProbeType::Call, + ..base + }, } } diff --git a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs index 6c049c2635..b838b04fd9 100644 --- a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs +++ b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs @@ -1,10 +1,11 @@ -use crate::spec::Target; +use crate::spec::{StackProbeType, Target}; pub fn target() -> Target { let mut base = super::fuchsia_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-fuchsia".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs index 2732716017..f32818806f 100644 --- a/compiler/rustc_target/src/spec/x86_64_linux_android.rs +++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::android_base::opts(); @@ -7,7 +7,8 @@ pub fn target() -> Target { base.features = "+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-linux-android".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs index 095c6f15c7..b1dce60c9a 100644 --- a/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions}; +use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::netbsd_base::opts(); @@ -12,7 +12,8 @@ pub fn target() -> Target { base.has_rpath = false; base.position_independent_executables = false; base.disable_redzone = true; - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-rumprun-netbsd".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs index 6ccf78402e..0f7422d30a 100644 --- a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::solaris_base::opts(); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-pc-solaris".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs index 30aa290987..754f4733e5 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::dragonfly_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-unknown-dragonfly".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs index ee904d7624..055602c1cc 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-unknown-freebsd".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs index ea7e068e51..8b3c9f591e 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::haiku_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; // This option is required to build executables on Haiku x86_64 base.position_independent_executables = true; diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs index 4005aaf58b..69fcb4627f 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs @@ -1,11 +1,12 @@ -use crate::spec::Target; +use crate::spec::{StackProbeType, Target}; pub fn target() -> Target { let mut base = super::hermit_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.features = "+rdrnd,+rdseed".to_string(); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-unknown-hermit".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs index b72d529363..e2c18d3f88 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs @@ -1,4 +1,4 @@ -use crate::spec::Target; +use crate::spec::{StackProbeType, Target}; pub fn target() -> Target { let mut base = super::hermit_kernel_base::opts(); @@ -7,7 +7,8 @@ pub fn target() -> Target { base.features = "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" .to_string(); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-unknown-hermit".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs index f127dd49bc..2ba6d36848 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-unknown-linux-gnu".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs index 0cae575284..268f231b18 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mx32".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; base.has_elf_tls = false; // BUG(GabrielMajeri): disabling the PLT on x86_64 Linux with x32 ABI // breaks code gen. See LLVM bug 36743 diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs index 3669c10981..b4d704f9ec 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs @@ -1,11 +1,13 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; + base.static_position_independent_executables = true; Target { diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs index 7e91a6ddbe..a5d8800752 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions}; +use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-unknown-netbsd".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs index 0fe01f09c2..fa0b667281 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-unknown-openbsd".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs index cdd445b261..0a8d7b25cb 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::redox_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; Target { llvm_target: "x86_64-unknown-redox".to_string(), diff --git a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs index 163af6fd8e..a066f110e0 100644 --- a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs @@ -1,11 +1,12 @@ -use crate::spec::{LinkerFlavor, Target}; +use crate::spec::{LinkerFlavor, StackProbeType, Target}; pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - base.stack_probes = true; + // don't use probe-stack=inline-asm until rust-lang/rust#83139 is resolved. + base.stack_probes = StackProbeType::Call; base.disable_redzone = true; Target { diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 41184ce211..da66fbc858 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -162,7 +162,7 @@ impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> { /// 'b` (and hence, transitively, that `T: 'a`). This method would /// add those assumptions into the outlives-environment. /// - /// Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` + /// Tests: `src/test/ui/regions/regions-free-region-ordering-*.rs` fn add_implied_bounds( &mut self, infcx: &InferCtxt<'a, 'tcx>, diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 42509cd897..e1f8d59991 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -11,7 +11,6 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(array_value_iter)] #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(drain_filter)] diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index f5bc90e6f9..25ba489032 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -1153,7 +1153,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!("instantiate_opaque_types: ty_var={:?}", ty_var); for predicate in &bounds { - if let ty::PredicateAtom::Projection(projection) = predicate.skip_binders() { + if let ty::PredicateKind::Projection(projection) = predicate.kind().skip_binder() { if projection.ty.references_error() { // No point on adding these obligations since there's a type error involved. return ty_var; @@ -1251,18 +1251,18 @@ crate fn required_region_bounds( traits::elaborate_predicates(tcx, predicates) .filter_map(|obligation| { debug!("required_region_bounds(obligation={:?})", obligation); - match obligation.predicate.skip_binders() { - ty::PredicateAtom::Projection(..) - | ty::PredicateAtom::Trait(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::RegionOutlives(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Projection(..) + | ty::PredicateKind::Trait(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => { // Search for a bound of the form `erased_self_ty // : 'a`, but be wary of something like `for<'a> // erased_self_ty : 'a` (we interpret a diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 6ab16886ed..6593c1000f 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -35,10 +35,7 @@ pub enum AutoTraitResult { #[allow(dead_code)] impl AutoTraitResult { fn is_auto(&self) -> bool { - match *self { - AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl => true, - _ => false, - } + matches!(self, AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl) } } @@ -308,8 +305,8 @@ impl AutoTraitFinder<'tcx> { infcx.resolve_vars_if_possible(Obligation::new(dummy_cause.clone(), new_env, pred)); let result = select.select(&obligation); - match &result { - &Ok(Some(ref impl_source)) => { + match result { + Ok(Some(ref impl_source)) => { // If we see an explicit negative impl (e.g., `impl !Send for MyStruct`), // we immediately bail out, since it's impossible for us to continue. @@ -342,8 +339,8 @@ impl AutoTraitFinder<'tcx> { return None; } } - &Ok(None) => {} - &Err(SelectionError::Unimplemented) => { + Ok(None) => {} + Err(SelectionError::Unimplemented) => { if self.is_param_no_infer(pred.skip_binder().trait_ref.substs) { already_visited.remove(&pred); self.add_user_pred( @@ -417,9 +414,9 @@ impl AutoTraitFinder<'tcx> { let mut should_add_new = true; user_computed_preds.retain(|&old_pred| { if let ( - ty::PredicateAtom::Trait(new_trait, _), - ty::PredicateAtom::Trait(old_trait, _), - ) = (new_pred.skip_binders(), old_pred.skip_binders()) + ty::PredicateKind::Trait(new_trait, _), + ty::PredicateKind::Trait(old_trait, _), + ) = (new_pred.kind().skip_binder(), old_pred.kind().skip_binder()) { if new_trait.def_id() == old_trait.def_id() { let new_substs = new_trait.trait_ref.substs; @@ -601,10 +598,7 @@ impl AutoTraitFinder<'tcx> { } fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { - match *p.ty().skip_binder().kind() { - ty::Projection(proj) if proj == p.skip_binder().projection_ty => true, - _ => false, - } + matches!(*p.ty().skip_binder().kind(), ty::Projection(proj) if proj == p.skip_binder().projection_ty) } fn evaluate_nested_obligations( @@ -639,18 +633,16 @@ impl AutoTraitFinder<'tcx> { // We check this by calling is_of_param on the relevant types // from the various possible predicates - let bound_predicate = predicate.bound_atom(); + let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(p, _) => { - if self.is_param_no_infer(p.trait_ref.substs) - && !only_projections - && is_new_pred - { - self.add_user_pred(computed_preds, predicate); - } + ty::PredicateKind::Trait(p, _) => { + // Add this to `predicates` so that we end up calling `select` + // with it. If this predicate ends up being unimplemented, + // then `evaluate_predicates` will handle adding it the `ParamEnv` + // if possible. predicates.push_back(bound_predicate.rebind(p)); } - ty::PredicateAtom::Projection(p) => { + ty::PredicateKind::Projection(p) => { let p = bound_predicate.rebind(p); debug!( "evaluate_nested_obligations: examining projection predicate {:?}", @@ -780,13 +772,13 @@ impl AutoTraitFinder<'tcx> { } } } - ty::PredicateAtom::RegionOutlives(binder) => { + ty::PredicateKind::RegionOutlives(binder) => { let binder = bound_predicate.rebind(binder); if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() { return false; } } - ty::PredicateAtom::TypeOutlives(binder) => { + ty::PredicateKind::TypeOutlives(binder) => { let binder = bound_predicate.rebind(binder); match ( binder.no_bound_vars(), @@ -809,7 +801,7 @@ impl AutoTraitFinder<'tcx> { _ => {} }; } - ty::PredicateAtom::ConstEquate(c1, c2) => { + ty::PredicateKind::ConstEquate(c1, c2) => { let evaluate = |c: &'tcx ty::Const<'tcx>| { if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { match select.infcx().const_eval_resolve( @@ -869,7 +861,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionReplacer<'a, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { (match r { - &ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned(), + ty::ReVar(vid) => self.vid_to_region.get(vid).cloned(), _ => None, }) .unwrap_or_else(|| r.super_fold_with(self)) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 9324d55ac1..99b96f6096 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -193,10 +193,8 @@ fn overlap_within_probe( let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes(); debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes); - let involves_placeholder = match selcx.infcx().region_constraints_added_in_snapshot(snapshot) { - Some(true) => true, - _ => false, - }; + let involves_placeholder = + matches!(selcx.infcx().region_constraints_added_in_snapshot(snapshot), Some(true)); Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index fdb2361ba0..89820bb141 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -16,8 +16,7 @@ use rustc_infer::infer::InferCtxt; use rustc_middle::mir::abstract_const::{Node, NodeId}; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::{self, Rvalue, StatementKind, TerminatorKind}; -use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::subst::{Subst, SubstsRef}; use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_session::lint; use rustc_span::def_id::{DefId, LocalDefId}; @@ -41,20 +40,29 @@ pub fn is_const_evaluatable<'cx, 'tcx>( // We are looking at a generic abstract constant. Some(ct) => { for pred in param_env.caller_bounds() { - match pred.skip_binders() { - ty::PredicateAtom::ConstEvaluatable(b_def, b_substs) => { - debug!( - "is_const_evaluatable: caller_bound={:?}, {:?}", - b_def, b_substs - ); + match pred.kind().skip_binder() { + ty::PredicateKind::ConstEvaluatable(b_def, b_substs) => { if b_def == def && b_substs == substs { debug!("is_const_evaluatable: caller_bound ~~> ok"); return Ok(()); - } else if AbstractConst::new(tcx, b_def, b_substs)? - .map_or(false, |b_ct| try_unify(tcx, ct, b_ct)) - { - debug!("is_const_evaluatable: abstract_const ~~> ok"); - return Ok(()); + } + + if let Some(b_ct) = AbstractConst::new(tcx, b_def, b_substs)? { + // Try to unify with each subtree in the AbstractConst to allow for + // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` + // predicate for `(N + 1) * 2` + let result = + walk_abstract_const(tcx, b_ct, |b_ct| { + match try_unify(tcx, ct, b_ct) { + true => ControlFlow::BREAK, + false => ControlFlow::CONTINUE, + } + }); + + if let ControlFlow::Break(()) = result { + debug!("is_const_evaluatable: abstract_const ~~> ok"); + return Ok(()); + } } } _ => {} // don't care @@ -78,7 +86,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( Concrete, } let mut failure_kind = FailureKind::Concrete; - walk_abstract_const::(tcx, ct, |node| match node { + walk_abstract_const::(tcx, ct, |node| match node.root() { Node::Leaf(leaf) => { let leaf = leaf.subst(tcx, ct.substs); if leaf.has_infer_types_or_consts() { @@ -100,15 +108,24 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } FailureKind::MentionsParam => { // FIXME(const_evaluatable_checked): Better error message. - infcx - .tcx - .sess - .struct_span_err(span, "unconstrained generic constant") - .span_help( + let mut err = + infcx.tcx.sess.struct_span_err(span, "unconstrained generic constant"); + let const_span = tcx.def_span(def.did); + // FIXME(const_evaluatable_checked): Update this suggestion once + // explicit const evaluatable bounds are implemented. + if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(const_span) + { + err.span_help( tcx.def_span(def.did), + &format!("try adding a `where` bound using this expression: where [u8; {}]: Sized", snippet), + ); + } else { + err.span_help( + const_span, "consider adding a `where` bound for this expression", - ) - .emit(); + ); + } + err.emit(); return Err(ErrorHandled::Reported(ErrorReported)); } FailureKind::Concrete => { @@ -152,7 +169,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( if concrete.is_ok() && substs.has_param_types_or_consts() { match infcx.tcx.def_kind(def.did) { DefKind::AnonConst => { - let mir_body = infcx.tcx.optimized_mir_opt_const_arg(def); + let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(def); if mir_body.is_polymorphic { future_compat_lint(); @@ -580,15 +597,15 @@ pub fn walk_abstract_const<'tcx, R, F>( mut f: F, ) -> ControlFlow where - F: FnMut(Node<'tcx>) -> ControlFlow, + F: FnMut(AbstractConst<'tcx>) -> ControlFlow, { fn recurse<'tcx, R>( tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, - f: &mut dyn FnMut(Node<'tcx>) -> ControlFlow, + f: &mut dyn FnMut(AbstractConst<'tcx>) -> ControlFlow, ) -> ControlFlow { + f(ct)?; let root = ct.root(); - f(root)?; match root { Node::Leaf(_) => ControlFlow::CONTINUE, Node::Binop(_, l, r) => { @@ -609,9 +626,29 @@ where /// Tries to unify two abstract constants using structural equality. pub(super) fn try_unify<'tcx>( tcx: TyCtxt<'tcx>, - a: AbstractConst<'tcx>, - b: AbstractConst<'tcx>, + mut a: AbstractConst<'tcx>, + mut b: AbstractConst<'tcx>, ) -> bool { + // We substitute generics repeatedly to allow AbstractConsts to unify where a + // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. + // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] + while let Node::Leaf(a_ct) = a.root() { + let a_ct = a_ct.subst(tcx, a.substs); + match AbstractConst::from_const(tcx, a_ct) { + Ok(Some(a_act)) => a = a_act, + Ok(None) => break, + Err(_) => return true, + } + } + while let Node::Leaf(b_ct) = b.root() { + let b_ct = b_ct.subst(tcx, b.substs); + match AbstractConst::from_const(tcx, b_ct) { + Ok(Some(b_act)) => b = b_act, + Ok(None) => break, + Err(_) => return true, + } + } + match (a.root(), b.root()) { (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { let a_ct = a_ct.subst(tcx, a.substs); @@ -632,8 +669,6 @@ pub(super) fn try_unify<'tcx>( // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This // means that we only allow inference variables if they are equal. (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - // We may want to instead recurse into unevaluated constants here. That may require some - // care to prevent infinite recursion, so let's just ignore this for now. ( ty::ConstKind::Unevaluated(a_def, a_substs, None), ty::ConstKind::Unevaluated(b_def, b_substs, None), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 9feba7bfc4..d3b3403ac3 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -256,9 +256,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - let bound_predicate = obligation.predicate.bound_atom(); + let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(trait_predicate, _) => { + ty::PredicateKind::Trait(trait_predicate, _) => { let trait_predicate = bound_predicate.rebind(trait_predicate); let trait_predicate = self.resolve_vars_if_possible(trait_predicate); @@ -280,18 +280,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let OnUnimplementedNote { message, label, note, enclosing_scope } = self.on_unimplemented_note(trait_ref, obligation); let have_alt_message = message.is_some() || label.is_some(); - let is_try = self - .tcx - .sess - .source_map() - .span_to_snippet(span) - .map(|s| &s == "?") - .unwrap_or(false); - let is_from = self.tcx.get_diagnostic_item(sym::from_trait) - == Some(trait_ref.def_id()); + let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id()); let is_unsize = { Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() }; - let (message, note) = if is_try && is_from { + let (message, note) = if is_try_conversion { ( Some(format!( "`?` couldn't convert the error to `{}`", @@ -319,7 +311,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { )) ); - if is_try && is_from { + if is_try_conversion { let none_error = self .tcx .get_diagnostic_item(sym::none_error) @@ -525,14 +517,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err } - ty::PredicateAtom::Subtype(predicate) => { + ty::PredicateKind::Subtype(predicate) => { // Errors for Subtype predicates show up as // `FulfillmentErrorCode::CodeSubtypeError`, // not selection error. span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) } - ty::PredicateAtom::RegionOutlives(predicate) => { + ty::PredicateKind::RegionOutlives(predicate) => { let predicate = bound_predicate.rebind(predicate); let predicate = self.resolve_vars_if_possible(predicate); let err = self @@ -549,7 +541,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) } - ty::PredicateAtom::Projection(..) | ty::PredicateAtom::TypeOutlives(..) => { + ty::PredicateKind::Projection(..) | ty::PredicateKind::TypeOutlives(..) => { let predicate = self.resolve_vars_if_possible(obligation.predicate); struct_span_err!( self.tcx.sess, @@ -560,12 +552,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) } - ty::PredicateAtom::ObjectSafe(trait_def_id) => { + ty::PredicateKind::ObjectSafe(trait_def_id) => { let violations = self.tcx.object_safety_violations(trait_def_id); report_object_safety_error(self.tcx, span, trait_def_id, violations) } - ty::PredicateAtom::ClosureKind(closure_def_id, closure_substs, kind) => { + ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { let found_kind = self.closure_kind(closure_substs).unwrap(); let closure_span = self.tcx.sess.source_map().guess_head_span( @@ -597,23 +589,23 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Some(typeck_results) = self.in_progress_typeck_results { let typeck_results = typeck_results.borrow(); match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { - (ty::ClosureKind::FnOnce, Some((span, name))) => { + (ty::ClosureKind::FnOnce, Some((span, place))) => { err.span_label( *span, format!( "closure is `FnOnce` because it moves the \ variable `{}` out of its environment", - name + ty::place_to_string_for_capture(tcx, place) ), ); } - (ty::ClosureKind::FnMut, Some((span, name))) => { + (ty::ClosureKind::FnMut, Some((span, place))) => { err.span_label( *span, format!( "closure is `FnMut` because it mutates the \ variable `{}` here", - name + ty::place_to_string_for_capture(tcx, place) ), ); } @@ -625,7 +617,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - ty::PredicateAtom::WellFormed(ty) => { + ty::PredicateKind::WellFormed(ty) => { if !self.tcx.sess.opts.debugging_opts.chalk { // WF predicates cannot themselves make // errors. They can only block due to @@ -643,7 +635,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } - ty::PredicateAtom::ConstEvaluatable(..) => { + ty::PredicateKind::ConstEvaluatable(..) => { // Errors for `ConstEvaluatable` predicates show up as // `SelectionError::ConstEvalFailure`, // not `Unimplemented`. @@ -654,7 +646,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) } - ty::PredicateAtom::ConstEquate(..) => { + ty::PredicateKind::ConstEquate(..) => { // Errors for `ConstEquate` predicates show up as // `SelectionError::ConstEvalFailure`, // not `Unimplemented`. @@ -665,7 +657,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) } - ty::PredicateAtom::TypeWellFormedFromEnv(..) => span_bug!( + ty::PredicateKind::TypeWellFormedFromEnv(..) => span_bug!( span, "TypeWellFormedFromEnv predicate should only exist in the environment" ), @@ -838,7 +830,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .collect::>(), ), Node::Ctor(ref variant_data) => { - let span = variant_data.ctor_hir_id().map(|id| hir.span(id)).unwrap_or(DUMMY_SP); + let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); let span = sm.guess_head_span(span); (span, vec![ArgKind::empty(); variant_data.fields().len()]) } @@ -861,10 +853,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { let arg_length = arguments.len(); - let distinct = match &other[..] { - &[ArgKind::Tuple(..)] => true, - _ => false, - }; + let distinct = matches!(other, &[ArgKind::Tuple(..)]); match (arg_length, arguments.get(0)) { (1, Some(&ArgKind::Tuple(_, ref fields))) => { format!("a single {}-tuple as argument", fields.len()) @@ -1080,9 +1069,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { } // FIXME: It should be possible to deal with `ForAll` in a cleaner way. - let bound_error = error.bound_atom(); - let (cond, error) = match (cond.skip_binders(), bound_error.skip_binder()) { - (ty::PredicateAtom::Trait(..), ty::PredicateAtom::Trait(error, _)) => { + let bound_error = error.kind(); + let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) { + (ty::PredicateKind::Trait(..), ty::PredicateKind::Trait(error, _)) => { (cond, bound_error.rebind(error)) } _ => { @@ -1092,8 +1081,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { }; for obligation in super::elaborate_predicates(self.tcx, std::iter::once(cond)) { - let bound_predicate = obligation.predicate.bound_atom(); - if let ty::PredicateAtom::Trait(implication, _) = bound_predicate.skip_binder() { + let bound_predicate = obligation.predicate.kind(); + if let ty::PredicateKind::Trait(implication, _) = bound_predicate.skip_binder() { let error = error.to_poly_trait_ref(); let implication = bound_predicate.rebind(implication.trait_ref); // FIXME: I'm just not taking associated types at all here. @@ -1173,8 +1162,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { // // this can fail if the problem was higher-ranked, in which // cause I have no idea for a good error message. - let bound_predicate = predicate.bound_atom(); - if let ty::PredicateAtom::Projection(data) = bound_predicate.skip_binder() { + let bound_predicate = predicate.kind(); + if let ty::PredicateKind::Projection(data) = bound_predicate.skip_binder() { let mut selcx = SelectionContext::new(self); let (data, _) = self.replace_bound_vars_with_fresh_vars( obligation.cause.span, @@ -1201,12 +1190,12 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { normalized_ty, data.ty ); - let is_normalized_ty_expected = match &obligation.cause.code { + let is_normalized_ty_expected = !matches!( + obligation.cause.code, ObligationCauseCode::ItemObligation(_) - | ObligationCauseCode::BindingObligation(_, _) - | ObligationCauseCode::ObjectCastObligation(_) => false, - _ => true, - }; + | ObligationCauseCode::BindingObligation(_, _) + | ObligationCauseCode::ObjectCastObligation(_) + ); if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( is_normalized_ty_expected, @@ -1374,7 +1363,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { code: &ObligationCauseCode<'tcx>, ) -> Option<(String, Option)> { match code { - &ObligationCauseCode::BuiltinDerivedObligation(ref data) => { + ObligationCauseCode::BuiltinDerivedObligation(data) => { let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref); match self.get_parent_trait_ref(&data.parent_code) { Some(t) => Some(t), @@ -1466,9 +1455,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - let bound_predicate = predicate.bound_atom(); + let bound_predicate = predicate.kind(); let mut err = match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(data, _) => { + ty::PredicateKind::Trait(data, _) => { let trait_ref = bound_predicate.rebind(data.trait_ref); debug!("trait_ref {:?}", trait_ref); @@ -1512,11 +1501,18 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { // check upstream for type errors and don't add the obligations to // begin with in those cases. if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { - self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0282).emit(); + self.emit_inference_failure_err(body_id, span, subst, vec![], ErrorCode::E0282) + .emit(); return; } - let mut err = - self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0283); + let impl_candidates = self.find_similar_impl_candidates(trait_ref); + let mut err = self.emit_inference_failure_err( + body_id, + span, + subst, + impl_candidates, + ErrorCode::E0283, + ); err.note(&format!("cannot satisfy `{}`", predicate)); if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); @@ -1573,17 +1569,17 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { err } - ty::PredicateAtom::WellFormed(arg) => { + ty::PredicateKind::WellFormed(arg) => { // Same hacky approach as above to avoid deluging user // with error messages. if arg.references_error() || self.tcx.sess.has_errors() { return; } - self.emit_inference_failure_err(body_id, span, arg, ErrorCode::E0282) + self.emit_inference_failure_err(body_id, span, arg, vec![], ErrorCode::E0282) } - ty::PredicateAtom::Subtype(data) => { + ty::PredicateKind::Subtype(data) => { if data.references_error() || self.tcx.sess.has_errors() { // no need to overload user in such cases return; @@ -1591,9 +1587,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { let SubtypePredicate { a_is_expected: _, a, b } = data; // both must be type variables, or the other would've been instantiated assert!(a.is_ty_var() && b.is_ty_var()); - self.emit_inference_failure_err(body_id, span, a.into(), ErrorCode::E0282) + self.emit_inference_failure_err(body_id, span, a.into(), vec![], ErrorCode::E0282) } - ty::PredicateAtom::Projection(data) => { + ty::PredicateKind::Projection(data) => { let trait_ref = bound_predicate.rebind(data).to_poly_trait_ref(self.tcx); let self_ty = trait_ref.skip_binder().self_ty(); let ty = data.ty; @@ -1606,6 +1602,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { body_id, span, self_ty.into(), + vec![], ErrorCode::E0284, ); err.note(&format!("cannot satisfy `{}`", predicate)); @@ -1723,9 +1720,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, ) { let (pred, item_def_id, span) = - match (obligation.predicate.skip_binders(), obligation.cause.code.peel_derives()) { + match (obligation.predicate.kind().skip_binder(), obligation.cause.code.peel_derives()) + { ( - ty::PredicateAtom::Trait(pred, _), + ty::PredicateKind::Trait(pred, _), &ObligationCauseCode::BindingObligation(item_def_id, span), ) => (pred, item_def_id, span), _ => return, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 79fea83a66..2182800616 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -286,21 +286,32 @@ fn suggest_restriction( ); } else { // Trivial case: `T` needs an extra bound: `T: Bound`. - let (sp, suggestion) = match super_traits { - None => predicate_constraint( + let (sp, suggestion) = match ( + generics + .params + .iter() + .filter(|p| { + !matches!(p.kind, hir::GenericParamKind::Type { synthetic: Some(_), .. }) + }) + .next(), + super_traits, + ) { + (_, None) => predicate_constraint( generics, trait_ref.without_const().to_predicate(tcx).to_string(), ), - Some((ident, bounds)) => match bounds { - [.., bound] => ( - bound.span().shrink_to_hi(), - format!(" + {}", trait_ref.print_only_trait_path().to_string()), - ), - [] => ( - ident.span.shrink_to_hi(), - format!(": {}", trait_ref.print_only_trait_path().to_string()), - ), - }, + (None, Some((ident, []))) => ( + ident.span.shrink_to_hi(), + format!(": {}", trait_ref.print_only_trait_path().to_string()), + ), + (_, Some((_, [.., bounds]))) => ( + bounds.span().shrink_to_hi(), + format!(" + {}", trait_ref.print_only_trait_path().to_string()), + ), + (Some(_), Some((_, []))) => ( + generics.span.shrink_to_hi(), + format!(": {}", trait_ref.print_only_trait_path().to_string()), + ), }; err.span_suggestion_verbose( @@ -393,7 +404,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(_, _, generics, _, _) - | hir::ItemKind::Impl { generics, .. }, + | hir::ItemKind::Impl(hir::Impl { generics, .. }), .. }) if projection.is_some() => { // Missing restriction on associated type of type parameter (unmet projection). @@ -416,7 +427,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { | hir::ItemKind::Enum(_, generics) | hir::ItemKind::Union(_, generics) | hir::ItemKind::Trait(_, _, generics, ..) - | hir::ItemKind::Impl { generics, .. } + | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Fn(_, generics, _) | hir::ItemKind::TyAlias(_, generics) | hir::ItemKind::TraitAlias(generics, _) @@ -888,8 +899,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // no return, suggest removal of semicolon on last statement. // Once that is added, close #54771. if let Some(ref stmt) = blk.stmts.last() { - let sp = self.tcx.sess.source_map().end_point(stmt.span); - err.span_label(sp, "consider removing this semicolon"); + if let hir::StmtKind::Semi(_) = stmt.kind { + let sp = self.tcx.sess.source_map().end_point(stmt.span); + err.span_label(sp, "consider removing this semicolon"); + } } } } @@ -1292,8 +1305,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // the type. The last generator (`outer_generator` below) has information about where the // bound was introduced. At least one generator should be present for this diagnostic to be // modified. - let (mut trait_ref, mut target_ty) = match obligation.predicate.skip_binders() { - ty::PredicateAtom::Trait(p, _) => (Some(p.trait_ref), Some(p.self_ty())), + let (mut trait_ref, mut target_ty) = match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(p, _) => (Some(p.trait_ref), Some(p.self_ty())), _ => (None, None), }; let mut generator = None; @@ -1868,23 +1881,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::Coercion { source: _, target } => { err.note(&format!("required by cast to type `{}`", self.ty_to_string(target))); } - ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expressions) => { + ObligationCauseCode::RepeatVec => { err.note( "the `Copy` trait is required because the repeated element will be copied", ); - if suggest_const_in_array_repeat_expressions { - err.note( - "this array initializer can be evaluated at compile-time, see issue \ - #49147 \ - for more information", - ); - if tcx.sess.opts.unstable_features.is_nightly_build() { - err.help( - "add `#![feature(const_in_array_repeat_expressions)]` to the \ - crate attributes to enable", - ); - } - } } ObligationCauseCode::VariableType(hir_id) => { let parent_node = self.tcx.hir().get_parent_node(hir_id); @@ -2278,6 +2278,12 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> { self.visit_expr(expr); } } + hir::ExprKind::If(_, then, else_opt) if self.in_block_tail => { + self.visit_expr(then); + if let Some(el) = else_opt { + self.visit_expr(el); + } + } hir::ExprKind::Match(_, arms, _) if self.in_block_tail => { for arm in arms { self.visit_expr(arm.body); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a04f816b0f..d4ced20f86 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -345,12 +345,13 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { let infcx = self.selcx.infcx(); - match *obligation.predicate.kind() { - ty::PredicateKind::ForAll(binder) => match binder.skip_binder() { + let binder = obligation.predicate.kind(); + match binder.no_bound_vars() { + None => match binder.skip_binder() { // Evaluation will discard candidates using the leak check. // This means we need to pass it the bound version of our // predicate. - ty::PredicateAtom::Trait(trait_ref, _constness) => { + ty::PredicateKind::Trait(trait_ref, _constness) => { let trait_obligation = obligation.with(binder.rebind(trait_ref)); self.process_trait_obligation( @@ -359,7 +360,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { &mut pending_obligation.stalled_on, ) } - ty::PredicateAtom::Projection(data) => { + ty::PredicateKind::Projection(data) => { let project_obligation = obligation.with(binder.rebind(data)); self.process_projection_obligation( @@ -367,25 +368,25 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { &mut pending_obligation.stalled_on, ) } - ty::PredicateAtom::RegionOutlives(_) - | ty::PredicateAtom::TypeOutlives(_) - | ty::PredicateAtom::WellFormed(_) - | ty::PredicateAtom::ObjectSafe(_) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(_) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => { + ty::PredicateKind::RegionOutlives(_) + | ty::PredicateKind::TypeOutlives(_) + | ty::PredicateKind::WellFormed(_) + | ty::PredicateKind::ObjectSafe(_) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(_) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => { let pred = infcx.replace_bound_vars_with_placeholders(binder); ProcessResult::Changed(mk_pending(vec![ obligation.with(pred.to_predicate(self.selcx.tcx())), ])) } - ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } }, - ty::PredicateKind::Atom(atom) => match atom { - ty::PredicateAtom::Trait(data, _) => { + Some(pred) => match pred { + ty::PredicateKind::Trait(data, _) => { let trait_obligation = obligation.with(Binder::dummy(data)); self.process_trait_obligation( @@ -395,14 +396,14 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { ) } - ty::PredicateAtom::RegionOutlives(data) => { + ty::PredicateKind::RegionOutlives(data) => { match infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)) { Ok(()) => ProcessResult::Changed(vec![]), Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), } } - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(t_a, r_b)) => { + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(t_a, r_b)) => { if self.register_region_obligations { self.selcx.infcx().register_region_obligation_with_cause( t_a, @@ -413,7 +414,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { ProcessResult::Changed(vec![]) } - ty::PredicateAtom::Projection(ref data) => { + ty::PredicateKind::Projection(ref data) => { let project_obligation = obligation.with(Binder::dummy(*data)); self.process_projection_obligation( @@ -422,7 +423,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { ) } - ty::PredicateAtom::ObjectSafe(trait_def_id) => { + ty::PredicateKind::ObjectSafe(trait_def_id) => { if !self.selcx.tcx().is_object_safe(trait_def_id) { ProcessResult::Error(CodeSelectionError(Unimplemented)) } else { @@ -430,7 +431,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } - ty::PredicateAtom::ClosureKind(_, closure_substs, kind) => { + ty::PredicateKind::ClosureKind(_, closure_substs, kind) => { match self.selcx.infcx().closure_kind(closure_substs) { Some(closure_kind) => { if closure_kind.extends(kind) { @@ -443,7 +444,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } - ty::PredicateAtom::WellFormed(arg) => { + ty::PredicateKind::WellFormed(arg) => { match wf::obligations( self.selcx.infcx(), obligation.param_env, @@ -461,7 +462,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } - ty::PredicateAtom::Subtype(subtype) => { + ty::PredicateKind::Subtype(subtype) => { match self.selcx.infcx().subtype_predicate( &obligation.cause, obligation.param_env, @@ -487,7 +488,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } - ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { + ty::PredicateKind::ConstEvaluatable(def_id, substs) => { match const_evaluatable::is_const_evaluatable( self.selcx.infcx(), def_id, @@ -507,7 +508,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } - ty::PredicateAtom::ConstEquate(c1, c2) => { + ty::PredicateKind::ConstEquate(c1, c2) => { debug!(?c1, ?c2, "equating consts"); if self.selcx.tcx().features().const_evaluatable_checked { // FIXME: we probably should only try to unify abstract constants @@ -593,7 +594,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } } - ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } }, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 2fb9b3cd5d..f7c0bafff0 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -6,7 +6,7 @@ pub mod auto_trait; mod chalk_fulfill; pub mod codegen; mod coherence; -mod const_evaluatable; +pub mod const_evaluatable; mod engine; pub mod error_reporting; mod fulfill; @@ -323,9 +323,8 @@ pub fn normalize_param_env_or_error<'tcx>( // This works fairly well because trait matching does not actually care about param-env // TypeOutlives predicates - these are normally used by regionck. let outlives_predicates: Vec<_> = predicates - .drain_filter(|predicate| match predicate.skip_binders() { - ty::PredicateAtom::TypeOutlives(..) => true, - _ => false, + .drain_filter(|predicate| { + matches!(predicate.kind().skip_binder(), ty::PredicateKind::TypeOutlives(..)) }) .collect(); diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 8b6e30f34f..e155f0366e 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -257,13 +257,11 @@ fn predicates_reference_self( } fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> { - let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id)); tcx.associated_items(trait_def_id) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .flat_map(|item| tcx.explicit_item_bounds(item.def_id)) - .map(|&(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) - .filter_map(|predicate| predicate_references_self(tcx, predicate)) + .filter_map(|pred_span| predicate_references_self(tcx, *pred_span)) .collect() } @@ -273,12 +271,12 @@ fn predicate_references_self( ) -> Option { let self_ty = tcx.types.self_param; let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into()); - match predicate.skip_binders() { - ty::PredicateAtom::Trait(ref data, _) => { + match predicate.kind().skip_binder() { + ty::PredicateKind::Trait(ref data, _) => { // In the case of a trait predicate, we can skip the "self" type. if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None } } - ty::PredicateAtom::Projection(ref data) => { + ty::PredicateKind::Projection(ref data) => { // And similarly for projections. This should be redundant with // the previous check because any projection should have a // matching `Trait` predicate with the same inputs, but we do @@ -300,15 +298,15 @@ fn predicate_references_self( None } } - ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::TypeOutlives(..) - | ty::PredicateAtom::RegionOutlives(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, + ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } } @@ -328,20 +326,20 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { let predicates = tcx.predicates_of(def_id); let predicates = predicates.instantiate_identity(tcx).predicates; elaborate_predicates(tcx, predicates.into_iter()).any(|obligation| { - match obligation.predicate.skip_binders() { - ty::PredicateAtom::Trait(ref trait_pred, _) => { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(ref trait_pred, _) => { trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0) } - ty::PredicateAtom::Projection(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::RegionOutlives(..) - | ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::TypeOutlives(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => false, + ty::PredicateKind::Projection(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => false, } }) } @@ -828,7 +826,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>( // constants which are not considered const evaluatable. use rustc_middle::mir::abstract_const::Node; if let Ok(Some(ct)) = AbstractConst::from_const(self.tcx, ct) { - const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node { + const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node.root() { Node::Leaf(leaf) => { let leaf = leaf.subst(self.tcx, ct.substs); self.visit_const(leaf) @@ -843,13 +841,13 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>( } fn visit_predicate(&mut self, pred: ty::Predicate<'tcx>) -> ControlFlow { - if let ty::PredicateAtom::ConstEvaluatable(def, substs) = pred.skip_binders() { + if let ty::PredicateKind::ConstEvaluatable(def, substs) = pred.kind().skip_binder() { // FIXME(const_evaluatable_checked): We should probably deduplicate the logic for // `AbstractConst`s here, it might make sense to change `ConstEvaluatable` to // take a `ty::Const` instead. use rustc_middle::mir::abstract_const::Node; if let Ok(Some(ct)) = AbstractConst::new(self.tcx, def, substs) { - const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node { + const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node.root() { Node::Leaf(leaf) => { let leaf = leaf.subst(self.tcx, ct.substs); self.visit_const(leaf) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index f22b5b9661..6908480f43 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -625,7 +625,7 @@ fn prune_cache_value_obligations<'a, 'tcx>( .obligations .iter() .filter(|obligation| { - let bound_predicate = obligation.predicate.bound_atom(); + let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { // We found a `T: Foo` predicate, let's check // if `U` references any unresolved type @@ -636,7 +636,7 @@ fn prune_cache_value_obligations<'a, 'tcx>( // indirect obligations (e.g., we project to `?0`, // but we have `T: Foo` and `?1: Bar`). - ty::PredicateAtom::Projection(data) => { + ty::PredicateKind::Projection(data) => { infcx.unresolved_type_vars(&bound_predicate.rebind(data.ty)).is_some() } @@ -912,8 +912,8 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( let infcx = selcx.infcx(); for predicate in env_predicates { debug!(?predicate); - let bound_predicate = predicate.bound_atom(); - if let ty::PredicateAtom::Projection(data) = predicate.skip_binders() { + let bound_predicate = predicate.kind(); + if let ty::PredicateKind::Projection(data) = predicate.kind().skip_binder() { let data = bound_predicate.rebind(data); let same_def_id = data.projection_def_id() == obligation.predicate.item_def_id; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 93ddcb6855..de538c62c5 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -15,7 +15,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { // `&T`, accounts for about 60% percentage of the predicates // we have to prove. No need to canonicalize and all that for // such cases. - if let ty::PredicateAtom::Trait(trait_ref, _) = key.value.predicate.skip_binders() { + if let ty::PredicateKind::Trait(trait_ref, _) = key.value.predicate.kind().skip_binder() { if let Some(sized_def_id) = tcx.lang_items().sized_trait() { if trait_ref.def_id() == sized_def_id { if trait_ref.self_ty().is_trivially_sized(tcx) { diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index ca3369b8f1..f09ce8d64e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -247,7 +247,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - self.assemble_candidates_for_trait_alias(obligation, &mut candidates)?; + self.assemble_candidates_for_trait_alias(obligation, &mut candidates); // Other bounds. Consider both in-scope bounds from fn decl // and applicable impls. There is a certain set of precedence rules here. @@ -259,11 +259,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // User-defined copy impls are permitted, but only for // structs and enums. - self.assemble_candidates_from_impls(obligation, &mut candidates)?; + self.assemble_candidates_from_impls(obligation, &mut candidates); // For other types, we'll use the builtin rules. let copy_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates)?; + self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); } else if lang_items.discriminant_kind_trait() == Some(def_id) { // `DiscriminantKind` is automatically implemented for every type. candidates.vec.push(DiscriminantKindCandidate); @@ -271,7 +271,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Sized is never implementable by end-users, it is // always automatically computed. let sized_conditions = self.sized_conditions(obligation); - self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?; + self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); } else if lang_items.unsize_trait() == Some(def_id) { self.assemble_candidates_for_unsizing(obligation, &mut candidates); } else { @@ -280,13 +280,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` // types have builtin support for `Clone`. let clone_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; + self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates); } - self.assemble_generator_candidates(obligation, &mut candidates)?; - self.assemble_closure_candidates(obligation, &mut candidates)?; - self.assemble_fn_pointer_candidates(obligation, &mut candidates)?; - self.assemble_candidates_from_impls(obligation, &mut candidates)?; + self.assemble_generator_candidates(obligation, &mut candidates); + self.assemble_closure_candidates(obligation, &mut candidates); + self.assemble_fn_pointer_candidates(obligation, &mut candidates); + self.assemble_candidates_from_impls(obligation, &mut candidates); self.assemble_candidates_from_object_ty(obligation, &mut candidates); } @@ -295,7 +295,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Auto implementations have lower priority, so we only // consider triggering a default if there is no other impl that can apply. if candidates.vec.is_empty() { - self.assemble_candidates_from_auto_impls(obligation, &mut candidates)?; + self.assemble_candidates_from_auto_impls(obligation, &mut candidates); } debug!("candidate list size: {}", candidates.vec.len()); Ok(candidates) @@ -367,9 +367,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) { - return Ok(()); + return; } // Okay to skip binder because the substs on generator types never @@ -388,8 +388,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } _ => {} } - - Ok(()) } /// Checks for the artificial impl that the compiler will create for an obligation like `X : @@ -402,11 +400,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { let kind = match self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()) { Some(k) => k, None => { - return Ok(()); + return; } }; @@ -435,8 +433,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } _ => {} } - - Ok(()) } /// Implements one of the `Fn()` family for a fn pointer. @@ -444,10 +440,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { // We provide impl of all fn traits for fn pointers. if self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()).is_none() { - return Ok(()); + return; } // Okay to skip binder because what we are inspecting doesn't involve bound regions. @@ -485,8 +481,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } _ => {} } - - Ok(()) } /// Searches for impls that might apply to `obligation`. @@ -494,7 +488,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { debug!(?obligation, "assemble_candidates_from_impls"); // Essentially any user-written impl will match with an error type, @@ -504,7 +498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Since compilation is already guaranteed to fail, this is just // to try to show the 'nicest' possible errors to the user. if obligation.references_error() { - return Ok(()); + return; } self.tcx().for_each_relevant_impl( @@ -518,15 +512,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }); }, ); - - Ok(()) } fn assemble_candidates_from_auto_impls( &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { // Okay to skip binder here because the tests we do below do not involve bound regions. let self_ty = obligation.self_ty().skip_binder(); debug!(?self_ty, "assemble_candidates_from_auto_impls"); @@ -558,7 +550,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // where-clause or, in the case of an object type, // it could be that the object type lists the // trait (e.g., `Foo+Send : Send`). See - // `compile-fail/typeck-default-trait-impl-send-param.rs` + // `ui/typeck/typeck-default-trait-impl-send-param.rs` // for an example of a test case that exercises // this path. } @@ -585,8 +577,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => candidates.vec.push(AutoImplCandidate(def_id)), } } - - Ok(()) } /// Searches for impls that might apply to `obligation`. @@ -753,7 +743,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { // Okay to skip binder here because the tests we do below do not involve bound regions. let self_ty = obligation.self_ty().skip_binder(); debug!(?self_ty, "assemble_candidates_for_trait_alias"); @@ -763,8 +753,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if self.tcx().is_trait_alias(def_id) { candidates.vec.push(TraitAliasCandidate(def_id)); } - - Ok(()) } /// Assembles the trait which are built-in to the language itself: @@ -773,7 +761,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, conditions: BuiltinImplConditions<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { + ) { match conditions { BuiltinImplConditions::Where(nested) => { debug!(?nested, "builtin_bound"); @@ -787,7 +775,5 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.ambiguous = true; } } - - Ok(()) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 030c29171a..ed3e117fcf 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -432,7 +432,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .predicates .into_iter() { - if let ty::PredicateAtom::Trait(..) = super_trait.skip_binders() { + if let ty::PredicateKind::Trait(..) = super_trait.kind().skip_binder() { let normalized_super_trait = normalize_with_depth_to( self, obligation.param_env, @@ -641,7 +641,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligations.push(Obligation::new( obligation.cause.clone(), obligation.param_env, - ty::PredicateAtom::ClosureKind(closure_def_id, substs, kind) + ty::PredicateKind::ClosureKind(closure_def_id, substs, kind) .to_predicate(self.tcx()), )); } @@ -823,33 +823,59 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }, }; + // FIXME(eddyb) cache this (including computing `unsizing_params`) + // by putting it in a query; it would only need the `DefId` as it + // looks at declared field types, not anything substituted. + // The last field of the structure has to exist and contain type/const parameters. let (tail_field, prefix_fields) = def.non_enum_variant().fields.split_last().ok_or(Unimplemented)?; let tail_field_ty = tcx.type_of(tail_field.did); let mut unsizing_params = GrowableBitSet::new_empty(); - let mut found = false; - for arg in tail_field_ty.walk() { - if let Some(i) = maybe_unsizing_param_idx(arg) { - unsizing_params.insert(i); - found = true; + if tcx.features().relaxed_struct_unsize { + for arg in tail_field_ty.walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.insert(i); + } } - } - if !found { - return Err(Unimplemented); - } - // Ensure none of the other fields mention the parameters used - // in unsizing. - // FIXME(eddyb) cache this (including computing `unsizing_params`) - // by putting it in a query; it would only need the `DefId` as it - // looks at declared field types, not anything substituted. - for field in prefix_fields { - for arg in tcx.type_of(field.did).walk() { + // Ensure none of the other fields mention the parameters used + // in unsizing. + for field in prefix_fields { + for arg in tcx.type_of(field.did).walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.remove(i); + } + } + } + + if unsizing_params.is_empty() { + return Err(Unimplemented); + } + } else { + let mut found = false; + for arg in tail_field_ty.walk() { if let Some(i) = maybe_unsizing_param_idx(arg) { - if unsizing_params.contains(i) { - return Err(Unimplemented); + unsizing_params.insert(i); + found = true; + } + } + if !found { + return Err(Unimplemented); + } + + // Ensure none of the other fields mention the parameters used + // in unsizing. + // FIXME(eddyb) cache this (including computing `unsizing_params`) + // by putting it in a query; it would only need the `DefId` as it + // looks at declared field types, not anything substituted. + for field in prefix_fields { + for arg in tcx.type_of(field.did).walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + if unsizing_params.contains(i) { + return Err(Unimplemented); + } } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index f1c86eab09..87c8099dc3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -69,16 +69,16 @@ impl IntercrateAmbiguityCause { pub fn intercrate_ambiguity_hint(&self) -> String { match self { - &IntercrateAmbiguityCause::DownstreamCrate { ref trait_desc, ref self_desc } => { - let self_desc = if let &Some(ref ty) = self_desc { + IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } => { + let self_desc = if let Some(ty) = self_desc { format!(" for type `{}`", ty) } else { String::new() }; format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc) } - &IntercrateAmbiguityCause::UpstreamCrateUpdate { ref trait_desc, ref self_desc } => { - let self_desc = if let &Some(ref ty) = self_desc { + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } => { + let self_desc = if let Some(ty) = self_desc { format!(" for type `{}`", ty) } else { String::new() @@ -89,7 +89,7 @@ impl IntercrateAmbiguityCause { trait_desc, self_desc ) } - &IntercrateAmbiguityCause::ReservationImpl { ref message } => message.clone(), + IntercrateAmbiguityCause::ReservationImpl { message } => message.clone(), } } } @@ -450,16 +450,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let result = ensure_sufficient_stack(|| { - let bound_predicate = obligation.predicate.bound_atom(); + let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(t, _) => { + ty::PredicateKind::Trait(t, _) => { let t = bound_predicate.rebind(t); debug_assert!(!t.has_escaping_bound_vars()); let obligation = obligation.with(t); self.evaluate_trait_predicate_recursively(previous_stack, obligation) } - ty::PredicateAtom::Subtype(p) => { + ty::PredicateKind::Subtype(p) => { let p = bound_predicate.rebind(p); // Does this code ever run? match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { @@ -475,7 +475,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateAtom::WellFormed(arg) => match wf::obligations( + ty::PredicateKind::WellFormed(arg) => match wf::obligations( self.infcx, obligation.param_env, obligation.cause.body_id, @@ -490,12 +490,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { None => Ok(EvaluatedToAmbig), }, - ty::PredicateAtom::TypeOutlives(..) | ty::PredicateAtom::RegionOutlives(..) => { + ty::PredicateKind::TypeOutlives(..) | ty::PredicateKind::RegionOutlives(..) => { // We do not consider region relationships when evaluating trait matches. Ok(EvaluatedToOkModuloRegions) } - ty::PredicateAtom::ObjectSafe(trait_def_id) => { + ty::PredicateKind::ObjectSafe(trait_def_id) => { if self.tcx().is_object_safe(trait_def_id) { Ok(EvaluatedToOk) } else { @@ -503,7 +503,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateAtom::Projection(data) => { + ty::PredicateKind::Projection(data) => { let data = bound_predicate.rebind(data); let project_obligation = obligation.with(data); match project::poly_project_and_unify_type(self, &project_obligation) { @@ -524,7 +524,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateAtom::ClosureKind(_, closure_substs, kind) => { + ty::PredicateKind::ClosureKind(_, closure_substs, kind) => { match self.infcx.closure_kind(closure_substs) { Some(closure_kind) => { if closure_kind.extends(kind) { @@ -537,7 +537,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { + ty::PredicateKind::ConstEvaluatable(def_id, substs) => { match const_evaluatable::is_const_evaluatable( self.infcx, def_id, @@ -551,7 +551,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateAtom::ConstEquate(c1, c2) => { + ty::PredicateKind::ConstEquate(c1, c2) => { debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts"); let evaluate = |c: &'tcx ty::Const<'tcx>| { @@ -594,7 +594,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } - ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for chalk") } } @@ -841,8 +841,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { - let result = match predicate.skip_binders() { - ty::PredicateAtom::Trait(ref data, _) => self.tcx().trait_is_auto(data.def_id()), + let result = match predicate.kind().skip_binder() { + ty::PredicateKind::Trait(ref data, _) => self.tcx().trait_is_auto(data.def_id()), _ => false, }; debug!(?predicate, ?result, "coinductive_predicate"); @@ -1170,8 +1170,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .iter() .enumerate() .filter_map(|(idx, bound)| { - let bound_predicate = bound.bound_atom(); - if let ty::PredicateAtom::Trait(pred, _) = bound_predicate.skip_binder() { + let bound_predicate = bound.kind(); + if let ty::PredicateKind::Trait(pred, _) = bound_predicate.skip_binder() { let bound = bound_predicate.rebind(pred.trait_ref); if self.infcx.probe(|_| { match self.match_normalize_trait_ref( diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 3f58fd72f4..e6ef9b137d 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -106,28 +106,28 @@ pub fn predicate_obligations<'a, 'tcx>( }; // It's ok to skip the binder here because wf code is prepared for it - match predicate.skip_binders() { - ty::PredicateAtom::Trait(t, _) => { + match predicate.kind().skip_binder() { + ty::PredicateKind::Trait(t, _) => { wf.compute_trait_ref(&t.trait_ref, Elaborate::None); } - ty::PredicateAtom::RegionOutlives(..) => {} - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { + ty::PredicateKind::RegionOutlives(..) => {} + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { wf.compute(ty.into()); } - ty::PredicateAtom::Projection(t) => { + ty::PredicateKind::Projection(t) => { wf.compute_projection(t.projection_ty); wf.compute(t.ty.into()); } - ty::PredicateAtom::WellFormed(arg) => { + ty::PredicateKind::WellFormed(arg) => { wf.compute(arg); } - ty::PredicateAtom::ObjectSafe(_) => {} - ty::PredicateAtom::ClosureKind(..) => {} - ty::PredicateAtom::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) => { + ty::PredicateKind::ObjectSafe(_) => {} + ty::PredicateKind::ClosureKind(..) => {} + ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) => { wf.compute(a.into()); wf.compute(b.into()); } - ty::PredicateAtom::ConstEvaluatable(def, substs) => { + ty::PredicateKind::ConstEvaluatable(def, substs) => { let obligations = wf.nominal_obligations(def.did, substs); wf.out.extend(obligations); @@ -135,11 +135,11 @@ pub fn predicate_obligations<'a, 'tcx>( wf.compute(arg); } } - ty::PredicateAtom::ConstEquate(c1, c2) => { + ty::PredicateKind::ConstEquate(c1, c2) => { wf.compute(c1.into()); wf.compute(c2.into()); } - ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } } @@ -199,7 +199,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( trait_ref, item, cause, pred ); let items = match item { - Some(hir::Item { kind: hir::ItemKind::Impl { items, .. }, .. }) => items, + Some(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.items, _ => return, }; let fix_span = @@ -209,8 +209,8 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( }; // It is fine to skip the binder as we don't care about regions here. - match pred.skip_binders() { - ty::PredicateAtom::Projection(proj) => { + match pred.kind().skip_binder() { + ty::PredicateKind::Projection(proj) => { // The obligation comes not from the current `impl` nor the `trait` being implemented, // but rather from a "second order" obligation, where an associated type has a // projection coming from another associated type. See @@ -225,7 +225,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( } } } - ty::PredicateAtom::Trait(pred, _) => { + ty::PredicateKind::Trait(pred, _) => { // An associated item obligation born out of the `trait` failed to be met. An example // can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`. debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred); @@ -333,7 +333,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let mut new_cause = cause.clone(); // The first subst is the self ty - use the correct span for it. if i == 0 { - if let Some(hir::ItemKind::Impl { self_ty, .. }) = item.map(|i| &i.kind) { + if let Some(hir::ItemKind::Impl(hir::Impl { self_ty, .. })) = + item.map(|i| &i.kind) + { new_cause.make_mut().span = self_ty.span; } } @@ -341,7 +343,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { new_cause, depth, param_env, - ty::PredicateAtom::WellFormed(arg).to_predicate(tcx), + ty::PredicateKind::WellFormed(arg).to_predicate(tcx), ) }), ); @@ -391,7 +393,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause.clone(), depth, param_env, - ty::PredicateAtom::WellFormed(arg).to_predicate(tcx), + ty::PredicateKind::WellFormed(arg).to_predicate(tcx), ) }), ); @@ -434,7 +436,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let obligations = self.nominal_obligations(def.did, substs); self.out.extend(obligations); - let predicate = ty::PredicateAtom::ConstEvaluatable(def, substs) + let predicate = ty::PredicateKind::ConstEvaluatable(def, substs) .to_predicate(self.tcx()); let cause = self.cause(traits::MiscObligation); self.out.push(traits::Obligation::with_depth( @@ -458,7 +460,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, self.recursion_depth, self.param_env, - ty::PredicateAtom::WellFormed(resolved_constant.into()) + ty::PredicateKind::WellFormed(resolved_constant.into()) .to_predicate(self.tcx()), )); } @@ -545,7 +547,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, depth, param_env, - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(rty, r)) + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(rty, r)) .to_predicate(self.tcx()), )); } @@ -635,7 +637,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause.clone(), depth, param_env, - ty::PredicateAtom::ObjectSafe(did).to_predicate(tcx), + ty::PredicateKind::ObjectSafe(did).to_predicate(tcx), ) })); } @@ -662,7 +664,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, self.recursion_depth, param_env, - ty::PredicateAtom::WellFormed(ty.into()).to_predicate(self.tcx()), + ty::PredicateKind::WellFormed(ty.into()).to_predicate(self.tcx()), )); } else { // Yes, resolved, proceed with the result. diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml index 8bd9e29629..8fdbc3b76b 100644 --- a/compiler/rustc_traits/Cargo.toml +++ b/compiler/rustc_traits/Cargo.toml @@ -6,15 +6,16 @@ edition = "2018" [dependencies] tracing = "0.1" +rustc_attr = { path = "../rustc_attr" } rustc_middle = { path = "../rustc_middle" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -chalk-ir = "0.36.0" -chalk-solve = "0.36.0" -chalk-engine = "0.36.0" +chalk-ir = "0.55.0" +chalk-solve = "0.55.0" +chalk-engine = "0.55.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_infer = { path = "../rustc_infer" } rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index 1893d74335..916186f420 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -10,6 +10,9 @@ use rustc_middle::traits::ChalkRustInterner as RustInterner; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::{self, AssocItemContainer, AssocKind, TyCtxt, TypeFoldable}; +use rustc_ast::ast; +use rustc_attr as attr; + use rustc_hir::def_id::DefId; use rustc_span::symbol::sym; @@ -18,7 +21,6 @@ use std::fmt; use std::sync::Arc; use crate::chalk::lowering::{self, LowerInto}; -use rustc_ast::ast; pub struct RustIrDatabase<'tcx> { pub(crate) interner: RustInterner<'tcx>, @@ -205,12 +207,32 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t fn adt_repr( &self, adt_id: chalk_ir::AdtId>, - ) -> chalk_solve::rust_ir::AdtRepr { + ) -> Arc>> { let adt_def = adt_id.0; - chalk_solve::rust_ir::AdtRepr { - repr_c: adt_def.repr.c(), - repr_packed: adt_def.repr.packed(), - } + let int = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Int(i)).intern(&self.interner); + let uint = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(i)).intern(&self.interner); + Arc::new(chalk_solve::rust_ir::AdtRepr { + c: adt_def.repr.c(), + packed: adt_def.repr.packed(), + int: adt_def.repr.int.map(|i| match i { + attr::IntType::SignedInt(ty) => match ty { + ast::IntTy::Isize => int(chalk_ir::IntTy::Isize), + ast::IntTy::I8 => int(chalk_ir::IntTy::I8), + ast::IntTy::I16 => int(chalk_ir::IntTy::I16), + ast::IntTy::I32 => int(chalk_ir::IntTy::I32), + ast::IntTy::I64 => int(chalk_ir::IntTy::I64), + ast::IntTy::I128 => int(chalk_ir::IntTy::I128), + }, + attr::IntType::UnsignedInt(ty) => match ty { + ast::UintTy::Usize => uint(chalk_ir::UintTy::Usize), + ast::UintTy::U8 => uint(chalk_ir::UintTy::U8), + ast::UintTy::U16 => uint(chalk_ir::UintTy::U16), + ast::UintTy::U32 => uint(chalk_ir::UintTy::U32), + ast::UintTy::U64 => uint(chalk_ir::UintTy::U64), + ast::UintTy::U128 => uint(chalk_ir::UintTy::U128), + }, + }), + }) } fn fn_def_datum( @@ -316,7 +338,11 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t let self_ty = self_ty.fold_with(&mut regions_substitutor); let lowered_ty = self_ty.lower_into(&self.interner); - parameters[0].assert_ty_ref(&self.interner).could_match(&self.interner, &lowered_ty) + parameters[0].assert_ty_ref(&self.interner).could_match( + &self.interner, + self.unification_database(), + &lowered_ty, + ) }); let impls = matched_impls.map(chalk_ir::ImplId).collect(); @@ -346,26 +372,26 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t (ty::Char, Scalar(Char)) => true, (ty::Int(ty1), Scalar(Int(ty2))) => matches!( (ty1, ty2), - (ast::IntTy::Isize, chalk_ir::IntTy::Isize) - | (ast::IntTy::I8, chalk_ir::IntTy::I8) - | (ast::IntTy::I16, chalk_ir::IntTy::I16) - | (ast::IntTy::I32, chalk_ir::IntTy::I32) - | (ast::IntTy::I64, chalk_ir::IntTy::I64) - | (ast::IntTy::I128, chalk_ir::IntTy::I128) + (ty::IntTy::Isize, chalk_ir::IntTy::Isize) + | (ty::IntTy::I8, chalk_ir::IntTy::I8) + | (ty::IntTy::I16, chalk_ir::IntTy::I16) + | (ty::IntTy::I32, chalk_ir::IntTy::I32) + | (ty::IntTy::I64, chalk_ir::IntTy::I64) + | (ty::IntTy::I128, chalk_ir::IntTy::I128) ), (ty::Uint(ty1), Scalar(Uint(ty2))) => matches!( (ty1, ty2), - (ast::UintTy::Usize, chalk_ir::UintTy::Usize) - | (ast::UintTy::U8, chalk_ir::UintTy::U8) - | (ast::UintTy::U16, chalk_ir::UintTy::U16) - | (ast::UintTy::U32, chalk_ir::UintTy::U32) - | (ast::UintTy::U64, chalk_ir::UintTy::U64) - | (ast::UintTy::U128, chalk_ir::UintTy::U128) + (ty::UintTy::Usize, chalk_ir::UintTy::Usize) + | (ty::UintTy::U8, chalk_ir::UintTy::U8) + | (ty::UintTy::U16, chalk_ir::UintTy::U16) + | (ty::UintTy::U32, chalk_ir::UintTy::U32) + | (ty::UintTy::U64, chalk_ir::UintTy::U64) + | (ty::UintTy::U128, chalk_ir::UintTy::U128) ), (ty::Float(ty1), Scalar(Float(ty2))) => matches!( (ty1, ty2), - (ast::FloatTy::F32, chalk_ir::FloatTy::F32) - | (ast::FloatTy::F64, chalk_ir::FloatTy::F64) + (ty::FloatTy::F32, chalk_ir::FloatTy::F32) + | (ty::FloatTy::F64, chalk_ir::FloatTy::F64) ), (&ty::Tuple(substs), Tuple(len, _)) => substs.len() == *len, (&ty::Array(..), Array(..)) => true, @@ -541,6 +567,7 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t Unsize => lang_items.unsize_trait(), Unpin => lang_items.unpin_trait(), CoerceUnsized => lang_items.coerce_unsized_trait(), + DiscriminantKind => lang_items.discriminant_kind_trait(), }; def_id.map(chalk_ir::TraitId) } @@ -586,7 +613,7 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t let sig = &substs.as_slice(&self.interner)[substs.len(&self.interner) - 2]; match sig.assert_ty_ref(&self.interner).kind(&self.interner) { chalk_ir::TyKind::Function(f) => { - let substitution = f.substitution.as_slice(&self.interner); + let substitution = f.substitution.0.as_slice(&self.interner); let return_type = substitution.last().unwrap().assert_ty_ref(&self.interner).clone(); // Closure arguments are tupled @@ -644,6 +671,51 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t ) -> Arc>> { unimplemented!() } + + fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase> { + self + } + + fn discriminant_type( + &self, + _: chalk_ir::Ty>, + ) -> chalk_ir::Ty> { + unimplemented!() + } +} + +impl<'tcx> chalk_ir::UnificationDatabase> for RustIrDatabase<'tcx> { + fn fn_def_variance( + &self, + def_id: chalk_ir::FnDefId>, + ) -> chalk_ir::Variances> { + let variances = self.interner.tcx.variances_of(def_id.0); + chalk_ir::Variances::from_iter( + &self.interner, + variances.iter().map(|v| match v { + ty::Variance::Invariant => chalk_ir::Variance::Invariant, + ty::Variance::Covariant => chalk_ir::Variance::Covariant, + ty::Variance::Contravariant => chalk_ir::Variance::Contravariant, + ty::Variance::Bivariant => unimplemented!(), + }), + ) + } + + fn adt_variance( + &self, + def_id: chalk_ir::AdtId>, + ) -> chalk_ir::Variances> { + let variances = self.interner.tcx.variances_of(def_id.0.did); + chalk_ir::Variances::from_iter( + &self.interner, + variances.iter().map(|v| match v { + ty::Variance::Invariant => chalk_ir::Variance::Invariant, + ty::Variance::Covariant => chalk_ir::Variance::Covariant, + ty::Variance::Contravariant => chalk_ir::Variance::Contravariant, + ty::Variance::Bivariant => unimplemented!(), + }), + ) + } } /// Creates a `InternalSubsts` that maps each generic parameter to a higher-ranked diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 8aa68e533a..7d3589c4b6 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -81,39 +81,36 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment, ) -> chalk_ir::InEnvironment>> { let clauses = self.environment.into_iter().map(|predicate| { - let (predicate, binders, _named_regions) = collect_bound_vars( - interner, - interner.tcx, - predicate.bound_atom_with_opt_escaping(interner.tcx), - ); + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, predicate.kind()); let consequence = match predicate { - ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { + ty::PredicateKind::TypeWellFormedFromEnv(ty) => { chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty(ty.lower_into(interner))) } - ty::PredicateAtom::Trait(predicate, _) => chalk_ir::DomainGoal::FromEnv( + ty::PredicateKind::Trait(predicate, _) => chalk_ir::DomainGoal::FromEnv( chalk_ir::FromEnv::Trait(predicate.trait_ref.lower_into(interner)), ), - ty::PredicateAtom::RegionOutlives(predicate) => chalk_ir::DomainGoal::Holds( + ty::PredicateKind::RegionOutlives(predicate) => chalk_ir::DomainGoal::Holds( chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { a: predicate.0.lower_into(interner), b: predicate.1.lower_into(interner), }), ), - ty::PredicateAtom::TypeOutlives(predicate) => chalk_ir::DomainGoal::Holds( + ty::PredicateKind::TypeOutlives(predicate) => chalk_ir::DomainGoal::Holds( chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { ty: predicate.0.lower_into(interner), lifetime: predicate.1.lower_into(interner), }), ), - ty::PredicateAtom::Projection(predicate) => chalk_ir::DomainGoal::Holds( + ty::PredicateKind::Projection(predicate) => chalk_ir::DomainGoal::Holds( chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)), ), - ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => bug!("unexpected predicate {}", predicate), + ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => bug!("unexpected predicate {}", predicate), }; let value = chalk_ir::ProgramClauseImplication { consequence, @@ -136,19 +133,16 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predicate<'tcx> { fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { - let (predicate, binders, _named_regions) = collect_bound_vars( - interner, - interner.tcx, - self.bound_atom_with_opt_escaping(interner.tcx), - ); + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, self.kind()); let value = match predicate { - ty::PredicateAtom::Trait(predicate, _) => { + ty::PredicateKind::Trait(predicate, _) => { chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)), )) } - ty::PredicateAtom::RegionOutlives(predicate) => { + ty::PredicateKind::RegionOutlives(predicate) => { chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { a: predicate.0.lower_into(interner), @@ -156,7 +150,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predi }), )) } - ty::PredicateAtom::TypeOutlives(predicate) => { + ty::PredicateKind::TypeOutlives(predicate) => { chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { ty: predicate.0.lower_into(interner), @@ -164,12 +158,12 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predi }), )) } - ty::PredicateAtom::Projection(predicate) => { + ty::PredicateKind::Projection(predicate) => { chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)), )) } - ty::PredicateAtom::WellFormed(arg) => match arg.unpack() { + ty::PredicateKind::WellFormed(arg) => match arg.unpack() { GenericArgKind::Type(ty) => match ty.kind() { // FIXME(chalk): In Chalk, a placeholder is WellFormed if it // `FromEnv`. However, when we "lower" Params, we don't update @@ -189,7 +183,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predi GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt), }, - ty::PredicateAtom::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal( + ty::PredicateKind::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal( chalk_ir::DomainGoal::ObjectSafe(chalk_ir::TraitId(t)), ), @@ -197,13 +191,13 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predi // // We can defer this, but ultimately we'll want to express // some of these in terms of chalk operations. - ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) => { + ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => { chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner)) } - ty::PredicateAtom::TypeWellFormedFromEnv(ty) => chalk_ir::GoalData::DomainGoal( + ty::PredicateKind::TypeWellFormedFromEnv(ty) => chalk_ir::GoalData::DomainGoal( chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty(ty.lower_into(interner))), ), }; @@ -239,8 +233,6 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq>> impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Ty> { - use rustc_ast as ast; - let int = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Int(i)); let uint = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(i)); let float = |f| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Float(f)); @@ -249,24 +241,24 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { ty::Bool => chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Bool), ty::Char => chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Char), ty::Int(ty) => match ty { - ast::IntTy::Isize => int(chalk_ir::IntTy::Isize), - ast::IntTy::I8 => int(chalk_ir::IntTy::I8), - ast::IntTy::I16 => int(chalk_ir::IntTy::I16), - ast::IntTy::I32 => int(chalk_ir::IntTy::I32), - ast::IntTy::I64 => int(chalk_ir::IntTy::I64), - ast::IntTy::I128 => int(chalk_ir::IntTy::I128), + ty::IntTy::Isize => int(chalk_ir::IntTy::Isize), + ty::IntTy::I8 => int(chalk_ir::IntTy::I8), + ty::IntTy::I16 => int(chalk_ir::IntTy::I16), + ty::IntTy::I32 => int(chalk_ir::IntTy::I32), + ty::IntTy::I64 => int(chalk_ir::IntTy::I64), + ty::IntTy::I128 => int(chalk_ir::IntTy::I128), }, ty::Uint(ty) => match ty { - ast::UintTy::Usize => uint(chalk_ir::UintTy::Usize), - ast::UintTy::U8 => uint(chalk_ir::UintTy::U8), - ast::UintTy::U16 => uint(chalk_ir::UintTy::U16), - ast::UintTy::U32 => uint(chalk_ir::UintTy::U32), - ast::UintTy::U64 => uint(chalk_ir::UintTy::U64), - ast::UintTy::U128 => uint(chalk_ir::UintTy::U128), + ty::UintTy::Usize => uint(chalk_ir::UintTy::Usize), + ty::UintTy::U8 => uint(chalk_ir::UintTy::U8), + ty::UintTy::U16 => uint(chalk_ir::UintTy::U16), + ty::UintTy::U32 => uint(chalk_ir::UintTy::U32), + ty::UintTy::U64 => uint(chalk_ir::UintTy::U64), + ty::UintTy::U128 => uint(chalk_ir::UintTy::U128), }, ty::Float(ty) => match ty { - ast::FloatTy::F32 => float(chalk_ir::FloatTy::F32), - ast::FloatTy::F64 => float(chalk_ir::FloatTy::F64), + ty::FloatTy::F32 => float(chalk_ir::FloatTy::F32), + ty::FloatTy::F64 => float(chalk_ir::FloatTy::F64), }, ty::Adt(def, substs) => { chalk_ir::TyKind::Adt(chalk_ir::AdtId(def), substs.lower_into(interner)) @@ -295,12 +287,12 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { chalk_ir::TyKind::Function(chalk_ir::FnPointer { num_binders: binders.len(interner), sig: sig.lower_into(interner), - substitution: chalk_ir::Substitution::from_iter( + substitution: chalk_ir::FnSubst(chalk_ir::Substitution::from_iter( interner, inputs_and_outputs.iter().map(|ty| { chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner) }), - ), + )), }) } ty::Dynamic(predicates, region) => chalk_ir::TyKind::Dyn(chalk_ir::DynTy { @@ -353,24 +345,24 @@ impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty> { chalk_ir::Scalar::Bool => ty::Bool, chalk_ir::Scalar::Char => ty::Char, chalk_ir::Scalar::Int(int_ty) => match int_ty { - chalk_ir::IntTy::Isize => ty::Int(ast::IntTy::Isize), - chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8), - chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16), - chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32), - chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64), - chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128), + chalk_ir::IntTy::Isize => ty::Int(ty::IntTy::Isize), + chalk_ir::IntTy::I8 => ty::Int(ty::IntTy::I8), + chalk_ir::IntTy::I16 => ty::Int(ty::IntTy::I16), + chalk_ir::IntTy::I32 => ty::Int(ty::IntTy::I32), + chalk_ir::IntTy::I64 => ty::Int(ty::IntTy::I64), + chalk_ir::IntTy::I128 => ty::Int(ty::IntTy::I128), }, chalk_ir::Scalar::Uint(int_ty) => match int_ty { - chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize), - chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8), - chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16), - chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32), - chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64), - chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128), + chalk_ir::UintTy::Usize => ty::Uint(ty::UintTy::Usize), + chalk_ir::UintTy::U8 => ty::Uint(ty::UintTy::U8), + chalk_ir::UintTy::U16 => ty::Uint(ty::UintTy::U16), + chalk_ir::UintTy::U32 => ty::Uint(ty::UintTy::U32), + chalk_ir::UintTy::U64 => ty::Uint(ty::UintTy::U64), + chalk_ir::UintTy::U128 => ty::Uint(ty::UintTy::U128), }, chalk_ir::Scalar::Float(float_ty) => match float_ty { - chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32), - chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64), + chalk_ir::FloatTy::F32 => ty::Float(ty::FloatTy::F32), + chalk_ir::FloatTy::F64 => ty::Float(ty::FloatTy::F64), }, }, TyKind::Array(ty, c) => { @@ -486,6 +478,10 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime ty::RegionKind::ReStatic, chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), + chalk_ir::LifetimeData::Empty(ui) => { + ty::RegionKind::ReEmpty(ty::UniverseIndex::from_usize(ui.counter)) + } + chalk_ir::LifetimeData::Erased => ty::RegionKind::ReErased, }; interner.tcx.mk_region(kind) } @@ -573,38 +569,35 @@ impl<'tcx> LowerInto<'tcx, Option, ) -> Option>> { - let (predicate, binders, _named_regions) = collect_bound_vars( - interner, - interner.tcx, - self.bound_atom_with_opt_escaping(interner.tcx), - ); + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, self.kind()); let value = match predicate { - ty::PredicateAtom::Trait(predicate, _) => { + ty::PredicateKind::Trait(predicate, _) => { Some(chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner))) } - ty::PredicateAtom::RegionOutlives(predicate) => { + ty::PredicateKind::RegionOutlives(predicate) => { Some(chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { a: predicate.0.lower_into(interner), b: predicate.1.lower_into(interner), })) } - ty::PredicateAtom::TypeOutlives(predicate) => { + ty::PredicateKind::TypeOutlives(predicate) => { Some(chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { ty: predicate.0.lower_into(interner), lifetime: predicate.1.lower_into(interner), })) } - ty::PredicateAtom::Projection(predicate) => { + ty::PredicateKind::Projection(predicate) => { Some(chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner))) } - ty::PredicateAtom::WellFormed(_ty) => None, - - ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + ty::PredicateKind::WellFormed(_ty) => None, + + ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("unexpected predicate {}", &self) } }; @@ -707,32 +700,29 @@ impl<'tcx> LowerInto<'tcx, Option, ) -> Option>> { - let (predicate, binders, _named_regions) = collect_bound_vars( - interner, - interner.tcx, - self.bound_atom_with_opt_escaping(interner.tcx), - ); + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, self.kind()); match predicate { - ty::PredicateAtom::Trait(predicate, _) => Some(chalk_ir::Binders::new( + ty::PredicateKind::Trait(predicate, _) => Some(chalk_ir::Binders::new( binders, chalk_solve::rust_ir::InlineBound::TraitBound( predicate.trait_ref.lower_into(interner), ), )), - ty::PredicateAtom::Projection(predicate) => Some(chalk_ir::Binders::new( + ty::PredicateKind::Projection(predicate) => Some(chalk_ir::Binders::new( binders, chalk_solve::rust_ir::InlineBound::AliasEqBound(predicate.lower_into(interner)), )), - ty::PredicateAtom::TypeOutlives(_predicate) => None, - ty::PredicateAtom::WellFormed(_ty) => None, - - ty::PredicateAtom::RegionOutlives(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + ty::PredicateKind::TypeOutlives(_predicate) => None, + ty::PredicateKind::WellFormed(_ty) => None, + + ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("unexpected predicate {}", &self) } } diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index f3a55fec9e..d98f18182c 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -98,21 +98,47 @@ crate fn evaluate_goal<'tcx>( let mut solver = chalk_engine::solve::SLGSolver::new(32, None); let db = ChalkRustIrDatabase { interner, reempty_placeholder }; let solution = solver.solve(&db, &lowered_goal); - debug!(?obligation, ?solution, "evaluatate goal"); + debug!(?obligation, ?solution, "evaluate goal"); // Ideally, the code to convert *back* to rustc types would live close to // the code to convert *from* rustc types. Right now though, we don't // really need this and so it's really minimal. // Right now, we also treat a `Unique` solution the same as // `Ambig(Definite)`. This really isn't right. - let make_solution = |subst: chalk_ir::Substitution<_>| { + let make_solution = |subst: chalk_ir::Substitution<_>, + binders: chalk_ir::CanonicalVarKinds<_>| { + use rustc_middle::infer::canonical::CanonicalVarInfo; + let mut var_values: IndexVec> = IndexVec::new(); subst.as_slice(&interner).iter().for_each(|p| { var_values.push(p.lower_into(&interner)); }); + let variables: Vec<_> = binders + .iter(&interner) + .map(|var| { + let kind = match var.kind { + chalk_ir::VariableKind::Ty(ty_kind) => CanonicalVarKind::Ty(match ty_kind { + chalk_ir::TyVariableKind::General => CanonicalTyVarKind::General( + ty::UniverseIndex::from_usize(var.skip_kind().counter), + ), + chalk_ir::TyVariableKind::Integer => CanonicalTyVarKind::Int, + chalk_ir::TyVariableKind::Float => CanonicalTyVarKind::Float, + }), + chalk_ir::VariableKind::Lifetime => CanonicalVarKind::Region( + ty::UniverseIndex::from_usize(var.skip_kind().counter), + ), + chalk_ir::VariableKind::Const(_) => CanonicalVarKind::Const( + ty::UniverseIndex::from_usize(var.skip_kind().counter), + ), + }; + CanonicalVarInfo { kind } + }) + .collect(); + let max_universe = + binders.iter(&interner).map(|v| v.skip_kind().counter).max().unwrap_or(0); let sol = Canonical { - max_universe: ty::UniverseIndex::from_usize(0), - variables: obligation.variables.clone(), + max_universe: ty::UniverseIndex::from_usize(max_universe), + variables: tcx.intern_canonical_var_infos(&variables), value: QueryResponse { var_values: CanonicalVarValues { var_values }, region_constraints: QueryRegionConstraints::default(), @@ -126,11 +152,13 @@ crate fn evaluate_goal<'tcx>( .map(|s| match s { Solution::Unique(subst) => { // FIXME(chalk): handle constraints - make_solution(subst.value.subst) + make_solution(subst.value.subst, subst.binders) } Solution::Ambig(guidance) => { match guidance { - chalk_solve::Guidance::Definite(subst) => make_solution(subst.value), + chalk_solve::Guidance::Definite(subst) => { + make_solution(subst.value, subst.binders) + } chalk_solve::Guidance::Suggested(_) => unimplemented!(), chalk_solve::Guidance::Unknown => { // chalk_fulfill doesn't use the var_values here, so diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 97017fbf2e..90ba90259c 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -94,27 +94,27 @@ fn compute_implied_outlives_bounds<'tcx>( // region relationships. implied_bounds.extend(obligations.into_iter().flat_map(|obligation| { assert!(!obligation.has_escaping_bound_vars()); - match obligation.predicate.kind() { - &ty::PredicateKind::ForAll(..) => vec![], - &ty::PredicateKind::Atom(atom) => match atom { - ty::PredicateAtom::Trait(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::Projection(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => vec![], - ty::PredicateAtom::WellFormed(arg) => { + match obligation.predicate.kind().no_bound_vars() { + None => vec![], + Some(pred) => match pred { + ty::PredicateKind::Trait(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => vec![], + ty::PredicateKind::WellFormed(arg) => { wf_args.push(arg); vec![] } - ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => { + ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => { vec![OutlivesBound::RegionSubRegion(r_b, r_a)] } - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty_a, r_b)) => { + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, r_b)) => { let ty_a = infcx.resolve_vars_if_possible(ty_a); let mut components = smallvec![]; tcx.push_outlives_components(ty_a, &mut components); diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 4841e4286a..1213e55390 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -46,16 +46,16 @@ fn normalize_generic_arg_after_erasing_regions<'tcx>( } fn not_outlives_predicate(p: &ty::Predicate<'tcx>) -> bool { - match p.skip_binders() { - ty::PredicateAtom::RegionOutlives(..) | ty::PredicateAtom::TypeOutlives(..) => false, - ty::PredicateAtom::Trait(..) - | ty::PredicateAtom::Projection(..) - | ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => true, + match p.kind().skip_binder() { + ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::TypeOutlives(..) => false, + ty::PredicateKind::Trait(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => true, } } diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 0addde5c44..6304f696b0 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -140,7 +140,7 @@ impl AscribeUserTypeCx<'me, 'tcx> { self.relate(self_ty, Variance::Invariant, impl_self_ty)?; self.prove_predicate( - ty::PredicateAtom::WellFormed(impl_self_ty.into()).to_predicate(self.tcx()), + ty::PredicateKind::WellFormed(impl_self_ty.into()).to_predicate(self.tcx()), ); } @@ -155,7 +155,7 @@ impl AscribeUserTypeCx<'me, 'tcx> { // them? This would only be relevant if some input // type were ill-formed but did not appear in `ty`, // which...could happen with normalization... - self.prove_predicate(ty::PredicateAtom::WellFormed(ty.into()).to_predicate(self.tcx())); + self.prove_predicate(ty::PredicateKind::WellFormed(ty.into()).to_predicate(self.tcx())); Ok(()) } } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index aa1de6d51c..77aa441340 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -5,7 +5,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_middle::hir::map as hir_map; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{ - self, Binder, Predicate, PredicateAtom, PredicateKind, ToPredicate, Ty, TyCtxt, WithConstness, + self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt, WithConstness, }; use rustc_session::CrateDisambiguator; use rustc_span::symbol::Symbol; @@ -129,8 +129,8 @@ fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { let parent_def_id = tcx.hir().local_def_id(parent_id); let parent_item = tcx.hir().expect_item(parent_id); match parent_item.kind { - hir::ItemKind::Impl { ref items, .. } => { - if let Some(impl_item_ref) = items.iter().find(|i| i.id.hir_id == id) { + hir::ItemKind::Impl(ref impl_) => { + if let Some(impl_item_ref) = impl_.items.iter().find(|i| i.id.hir_id == id) { let assoc_item = associated_item_from_impl_item_ref(tcx, parent_def_id, impl_item_ref); debug_assert_eq!(assoc_item.def_id, def_id); @@ -160,8 +160,8 @@ fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let item = tcx.hir().expect_item(hir_id); - if let hir::ItemKind::Impl { defaultness, .. } = item.kind { - defaultness + if let hir::ItemKind::Impl(impl_) = &item.kind { + impl_.defaultness } else { bug!("`impl_defaultness` called on {:?}", item); } @@ -201,8 +201,9 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { .map(|trait_item_ref| trait_item_ref.id) .map(|id| tcx.hir().local_def_id(id.hir_id).to_def_id()), ), - hir::ItemKind::Impl { ref items, .. } => tcx.arena.alloc_from_iter( - items + hir::ItemKind::Impl(ref impl_) => tcx.arena.alloc_from_iter( + impl_ + .items .iter() .map(|impl_item_ref| impl_item_ref.id) .map(|id| tcx.hir().local_def_id(id.hir_id).to_def_id()), @@ -217,8 +218,8 @@ fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssociatedItems<'_> { ty::AssociatedItems::new(items) } -fn def_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span { - tcx.hir().span_if_local(def_id).unwrap() +fn def_ident_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option { + tcx.hir().get_if_local(def_id).and_then(|node| node.ident()).map(|ident| ident.span) } /// If the given `DefId` describes an item belonging to a trait, @@ -323,8 +324,8 @@ fn well_formed_types_in_env<'tcx>( }, Node::Item(item) => match item.kind { - ItemKind::Impl { of_trait: Some(_), .. } => NodeKind::TraitImpl, - ItemKind::Impl { of_trait: None, .. } => NodeKind::InherentImpl, + ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => NodeKind::TraitImpl, + ItemKind::Impl(hir::Impl { of_trait: None, .. }) => NodeKind::InherentImpl, ItemKind::Fn(..) => NodeKind::Fn, _ => NodeKind::Other, }, @@ -373,8 +374,8 @@ fn well_formed_types_in_env<'tcx>( let input_clauses = inputs.into_iter().filter_map(|arg| { match arg.unpack() { GenericArgKind::Type(ty) => { - let binder = Binder::dummy(PredicateAtom::TypeWellFormedFromEnv(ty)); - Some(tcx.mk_predicate(PredicateKind::ForAll(binder))) + let binder = Binder::dummy(PredicateKind::TypeWellFormedFromEnv(ty)); + Some(tcx.mk_predicate(binder)) } // FIXME(eddyb) no WF conditions from lifetimes? @@ -490,7 +491,7 @@ pub fn provide(providers: &mut ty::query::Providers) { associated_item_def_ids, associated_items, adt_sized_constraint, - def_span, + def_ident_span, param_env, param_env_reveal_all_normalized, trait_of_item, diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index d50451b779..3f64bd8999 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -12,3 +12,4 @@ bitflags = "1.2.1" rustc_index = { path = "../rustc_index" } rustc_serialize = { path = "../rustc_serialize" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 37abb4496a..7e70af21c0 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -4,8 +4,13 @@ #[macro_use] extern crate bitflags; +#[macro_use] +extern crate rustc_macros; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::unify::{EqUnifyValue, UnifyKey}; +use std::fmt; +use std::mem::discriminant; bitflags! { /// Flags that we track on types. These flags are propagated upwards @@ -197,8 +202,409 @@ impl DebruijnIndex { } } +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Encodable, Decodable)] +pub enum IntTy { + Isize, + I8, + I16, + I32, + I64, + I128, +} + +impl IntTy { + pub fn name_str(&self) -> &'static str { + match *self { + IntTy::Isize => "isize", + IntTy::I8 => "i8", + IntTy::I16 => "i16", + IntTy::I32 => "i32", + IntTy::I64 => "i64", + IntTy::I128 => "i128", + } + } + + pub fn bit_width(&self) -> Option { + Some(match *self { + IntTy::Isize => return None, + IntTy::I8 => 8, + IntTy::I16 => 16, + IntTy::I32 => 32, + IntTy::I64 => 64, + IntTy::I128 => 128, + }) + } + + pub fn normalize(&self, target_width: u32) -> Self { + match self { + IntTy::Isize => match target_width { + 16 => IntTy::I16, + 32 => IntTy::I32, + 64 => IntTy::I64, + _ => unreachable!(), + }, + _ => *self, + } + } +} + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Debug)] +#[derive(Encodable, Decodable)] +pub enum UintTy { + Usize, + U8, + U16, + U32, + U64, + U128, +} + +impl UintTy { + pub fn name_str(&self) -> &'static str { + match *self { + UintTy::Usize => "usize", + UintTy::U8 => "u8", + UintTy::U16 => "u16", + UintTy::U32 => "u32", + UintTy::U64 => "u64", + UintTy::U128 => "u128", + } + } + + pub fn bit_width(&self) -> Option { + Some(match *self { + UintTy::Usize => return None, + UintTy::U8 => 8, + UintTy::U16 => 16, + UintTy::U32 => 32, + UintTy::U64 => 64, + UintTy::U128 => 128, + }) + } + + pub fn normalize(&self, target_width: u32) -> Self { + match self { + UintTy::Usize => match target_width { + 16 => UintTy::U16, + 32 => UintTy::U32, + 64 => UintTy::U64, + _ => unreachable!(), + }, + _ => *self, + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Encodable, Decodable)] +pub enum FloatTy { + F32, + F64, +} + +impl FloatTy { + pub fn name_str(self) -> &'static str { + match self { + FloatTy::F32 => "f32", + FloatTy::F64 => "f64", + } + } + + pub fn bit_width(self) -> u64 { + match self { + FloatTy::F32 => 32, + FloatTy::F64 => 64, + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum IntVarValue { + IntType(IntTy), + UintType(UintTy), +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct FloatVarValue(pub FloatTy); + +/// A **ty**pe **v**ariable **ID**. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] +pub struct TyVid { + pub index: u32, +} + +/// An **int**egral (`u32`, `i32`, `usize`, etc.) type **v**ariable **ID**. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] +pub struct IntVid { + pub index: u32, +} + +/// An **float**ing-point (`f32` or `f64`) type **v**ariable **ID**. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] +pub struct FloatVid { + pub index: u32, +} + +/// A placeholder for a type that hasn't been inferred yet. +/// +/// E.g., if we have an empty array (`[]`), then we create a fresh +/// type variable for the element type since we won't know until it's +/// used what the element type is supposed to be. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] +pub enum InferTy { + /// A type variable. + TyVar(TyVid), + /// An integral type variable (`{integer}`). + /// + /// These are created when the compiler sees an integer literal like + /// `1` that could be several different types (`u8`, `i32`, `u32`, etc.). + /// We don't know until it's used what type it's supposed to be, so + /// we create a fresh type variable. + IntVar(IntVid), + /// A floating-point type variable (`{float}`). + /// + /// These are created when the compiler sees an float literal like + /// `1.0` that could be either an `f32` or an `f64`. + /// We don't know until it's used what type it's supposed to be, so + /// we create a fresh type variable. + FloatVar(FloatVid), + + /// A [`FreshTy`][Self::FreshTy] is one that is generated as a replacement + /// for an unbound type variable. This is convenient for caching etc. See + /// `rustc_infer::infer::freshen` for more details. + /// + /// Compare with [`TyVar`][Self::TyVar]. + FreshTy(u32), + /// Like [`FreshTy`][Self::FreshTy], but as a replacement for [`IntVar`][Self::IntVar]. + FreshIntTy(u32), + /// Like [`FreshTy`][Self::FreshTy], but as a replacement for [`FloatVar`][Self::FloatVar]. + FreshFloatTy(u32), +} + +/// Raw `TyVid` are used as the unification key for `sub_relations`; +/// they carry no values. +impl UnifyKey for TyVid { + type Value = (); + fn index(&self) -> u32 { + self.index + } + fn from_index(i: u32) -> TyVid { + TyVid { index: i } + } + fn tag() -> &'static str { + "TyVid" + } +} + +impl EqUnifyValue for IntVarValue {} + +impl UnifyKey for IntVid { + type Value = Option; + fn index(&self) -> u32 { + self.index + } + fn from_index(i: u32) -> IntVid { + IntVid { index: i } + } + fn tag() -> &'static str { + "IntVid" + } +} + +impl EqUnifyValue for FloatVarValue {} + +impl UnifyKey for FloatVid { + type Value = Option; + fn index(&self) -> u32 { + self.index + } + fn from_index(i: u32) -> FloatVid { + FloatVid { index: i } + } + fn tag() -> &'static str { + "FloatVid" + } +} + +#[derive(Copy, Clone, PartialEq, Decodable, Encodable)] +pub enum Variance { + Covariant, // T <: T iff A <: B -- e.g., function return type + Invariant, // T <: T iff B == A -- e.g., type of mutable cell + Contravariant, // T <: T iff B <: A -- e.g., function param type + Bivariant, // T <: T -- e.g., unused type parameter +} + +impl Variance { + /// `a.xform(b)` combines the variance of a context with the + /// variance of a type with the following meaning. If we are in a + /// context with variance `a`, and we encounter a type argument in + /// a position with variance `b`, then `a.xform(b)` is the new + /// variance with which the argument appears. + /// + /// Example 1: + /// + /// *mut Vec + /// + /// Here, the "ambient" variance starts as covariant. `*mut T` is + /// invariant with respect to `T`, so the variance in which the + /// `Vec` appears is `Covariant.xform(Invariant)`, which + /// yields `Invariant`. Now, the type `Vec` is covariant with + /// respect to its type argument `T`, and hence the variance of + /// the `i32` here is `Invariant.xform(Covariant)`, which results + /// (again) in `Invariant`. + /// + /// Example 2: + /// + /// fn(*const Vec, *mut Vec` appears is + /// `Contravariant.xform(Covariant)` or `Contravariant`. The same + /// is true for its `i32` argument. In the `*mut T` case, the + /// variance of `Vec` is `Contravariant.xform(Invariant)`, + /// and hence the outermost type is `Invariant` with respect to + /// `Vec` (and its `i32` argument). + /// + /// Source: Figure 1 of "Taming the Wildcards: + /// Combining Definition- and Use-Site Variance" published in PLDI'11. + pub fn xform(self, v: Variance) -> Variance { + match (self, v) { + // Figure 1, column 1. + (Variance::Covariant, Variance::Covariant) => Variance::Covariant, + (Variance::Covariant, Variance::Contravariant) => Variance::Contravariant, + (Variance::Covariant, Variance::Invariant) => Variance::Invariant, + (Variance::Covariant, Variance::Bivariant) => Variance::Bivariant, + + // Figure 1, column 2. + (Variance::Contravariant, Variance::Covariant) => Variance::Contravariant, + (Variance::Contravariant, Variance::Contravariant) => Variance::Covariant, + (Variance::Contravariant, Variance::Invariant) => Variance::Invariant, + (Variance::Contravariant, Variance::Bivariant) => Variance::Bivariant, + + // Figure 1, column 3. + (Variance::Invariant, _) => Variance::Invariant, + + // Figure 1, column 4. + (Variance::Bivariant, _) => Variance::Bivariant, + } + } +} + impl HashStable for DebruijnIndex { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { self.as_u32().hash_stable(ctx, hasher); } } + +impl HashStable for IntTy { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + discriminant(self).hash_stable(ctx, hasher); + } +} + +impl HashStable for UintTy { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + discriminant(self).hash_stable(ctx, hasher); + } +} + +impl HashStable for FloatTy { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + discriminant(self).hash_stable(ctx, hasher); + } +} + +impl HashStable for InferTy { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + use InferTy::*; + match self { + TyVar(v) => v.index.hash_stable(ctx, hasher), + IntVar(v) => v.index.hash_stable(ctx, hasher), + FloatVar(v) => v.index.hash_stable(ctx, hasher), + FreshTy(v) | FreshIntTy(v) | FreshFloatTy(v) => v.hash_stable(ctx, hasher), + } + } +} + +impl HashStable for Variance { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + discriminant(self).hash_stable(ctx, hasher); + } +} + +impl fmt::Debug for IntVarValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + IntVarValue::IntType(ref v) => v.fmt(f), + IntVarValue::UintType(ref v) => v.fmt(f), + } + } +} + +impl fmt::Debug for FloatVarValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Debug for TyVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}t", self.index) + } +} + +impl fmt::Debug for IntVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}i", self.index) + } +} + +impl fmt::Debug for FloatVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}f", self.index) + } +} + +impl fmt::Debug for InferTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InferTy::*; + match *self { + TyVar(ref v) => v.fmt(f), + IntVar(ref v) => v.fmt(f), + FloatVar(ref v) => v.fmt(f), + FreshTy(v) => write!(f, "FreshTy({:?})", v), + FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), + FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v), + } + } +} + +impl fmt::Debug for Variance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + Variance::Covariant => "+", + Variance::Contravariant => "-", + Variance::Invariant => "o", + Variance::Bivariant => "*", + }) + } +} + +impl fmt::Display for InferTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InferTy::*; + match *self { + TyVar(_) => write!(f, "_"), + IntVar(_) => write!(f, "{}", "{integer}"), + FloatVar(_) => write!(f, "{}", "{float}"), + FreshTy(v) => write!(f, "FreshTy({})", v), + FreshIntTy(v) => write!(f, "FreshIntTy({})", v), + FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v), + } + } +} diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs index b04acd9660..545c30169b 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -96,7 +96,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let trait_def = self.tcx().trait_def(trait_def_id); if !self.tcx().features().unboxed_closures - && trait_segment.generic_args().parenthesized != trait_def.paren_sugar + && trait_segment.args().parenthesized != trait_def.paren_sugar { let sess = &self.tcx().sess.parse_sess; // For now, require that parenthetical notation be used only with `Fn()` etc. @@ -126,7 +126,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }) .unwrap_or_else(|| "()".to_string()), trait_segment - .generic_args() + .args() .bindings .iter() .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index b7e77f389f..67e37ca8d8 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -1,81 +1,92 @@ +use super::IsMethodCall; use crate::astconv::{ AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; use crate::errors::AssocTypeBindingNotAllowed; +use crate::structured_errors::{StructuredDiagnostic, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; +use rustc_errors::{struct_span_err, Applicability, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; use rustc_middle::ty::{ self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt, }; -use rustc_session::{lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, Session}; +use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; use rustc_span::{symbol::kw, MultiSpan, Span}; - use smallvec::SmallVec; impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Report an error that a generic argument did not match the generic parameter that was /// expected. fn generic_arg_mismatch_err( - sess: &Session, + tcx: TyCtxt<'_>, arg: &GenericArg<'_>, - kind: &'static str, + param: &GenericParamDef, possible_ordering_error: bool, help: Option<&str>, ) { + let sess = tcx.sess; let mut err = struct_span_err!( sess, arg.span(), E0747, "{} provided when a {} was expected", arg.descr(), - kind, + param.kind.descr(), ); - let unordered = sess.features_untracked().const_generics; - let kind_ord = match kind { - "lifetime" => ParamKindOrd::Lifetime, - "type" => ParamKindOrd::Type, - "constant" => ParamKindOrd::Const { unordered }, - // It's more concise to match on the string representation, though it means - // the match is non-exhaustive. - _ => bug!("invalid generic parameter kind {}", kind), - }; - - if let ParamKindOrd::Const { .. } = kind_ord { + if let GenericParamDefKind::Const { .. } = param.kind { if let GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. }) = arg { err.help("const arguments cannot yet be inferred with `_`"); } } - let arg_ord = match arg { - GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, - GenericArg::Type(_) => ParamKindOrd::Type, - GenericArg::Const(_) => ParamKindOrd::Const { unordered }, - }; - - if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. })) - && matches!(kind_ord, ParamKindOrd::Const { .. }) - { - let suggestions = vec![ - (arg.span().shrink_to_lo(), String::from("{ ")), - (arg.span().shrink_to_hi(), String::from(" }")), - ]; - err.multipart_suggestion( - "if this generic argument was intended as a const parameter, \ + // Specific suggestion set for diagnostics + match (arg, ¶m.kind) { + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. }), + GenericParamDefKind::Const { .. }, + ) => { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + err.multipart_suggestion( + "if this generic argument was intended as a const parameter, \ try surrounding it with braces:", - suggestions, - Applicability::MaybeIncorrect, - ); + suggestions, + Applicability::MaybeIncorrect, + ); + } + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), + GenericParamDefKind::Const { .. }, + ) if tcx.type_of(param.def_id) == tcx.types.usize => { + let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id)); + if let Ok(snippet) = snippet { + err.span_suggestion( + arg.span(), + "array type provided where a `usize` was expected, try", + format!("{{ {} }}", snippet), + Applicability::MaybeIncorrect, + ); + } + } + _ => {} } + let kind_ord = param.kind.to_ord(tcx); + let arg_ord = arg.to_ord(&tcx.features()); + // This note is only true when generic parameters are strictly ordered by their kind. if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { - let (first, last) = - if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) }; + let (first, last) = if kind_ord < arg_ord { + (param.kind.descr(), arg.descr()) + } else { + (arg.descr(), param.kind.descr()) + }; err.note(&format!("{} arguments must be provided before {} arguments", first, last)); if let Some(help) = help { err.help(help); @@ -203,7 +214,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // We expected a lifetime argument, but got a type or const // argument. That means we're inferring the lifetimes. substs.push(ctx.inferred_kind(None, param, infer_args)); - force_infer_lt = Some(arg); + force_infer_lt = Some((arg, param)); params.next(); } (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { @@ -213,7 +224,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // ignore it. args.next(); } - (_, kind, _) => { + (_, _, _) => { // We expected one kind of parameter, but the user provided // another. This is an error. However, if we already know that // the arguments don't match up with the parameters, we won't issue @@ -256,9 +267,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { param_types_present.dedup(); Self::generic_arg_mismatch_err( - tcx.sess, + tcx, arg, - kind.descr(), + param, !args_iter.clone().is_sorted_by_key(|arg| match arg { GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, GenericArg::Type(_) => ParamKindOrd::Type, @@ -315,9 +326,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { { let kind = arg.descr(); assert_eq!(kind, "lifetime"); - let provided = + let (provided_arg, param) = force_infer_lt.expect("lifetimes ought to have been inferred"); - Self::generic_arg_mismatch_err(tcx.sess, provided, kind, false, None); + Self::generic_arg_mismatch_err(tcx, provided_arg, param, false, None); } break; @@ -343,20 +354,25 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pub fn check_generic_arg_count_for_call( tcx: TyCtxt<'_>, span: Span, - def: &ty::Generics, + def_id: DefId, + generics: &ty::Generics, seg: &hir::PathSegment<'_>, - is_method_call: bool, + is_method_call: IsMethodCall, ) -> GenericArgCountResult { let empty_args = hir::GenericArgs::none(); - let suppress_mismatch = Self::check_impl_trait(tcx, seg, &def); + let suppress_mismatch = Self::check_impl_trait(tcx, seg, &generics); + + let gen_args = seg.args.unwrap_or(&empty_args); + let gen_pos = if is_method_call == IsMethodCall::Yes { + GenericArgPosition::MethodCall + } else { + GenericArgPosition::Value + }; + let has_self = generics.parent.is_none() && generics.has_self; + let infer_args = seg.infer_args || suppress_mismatch; + Self::check_generic_arg_count( - tcx, - span, - def, - if let Some(ref args) = seg.args { args } else { &empty_args }, - if is_method_call { GenericArgPosition::MethodCall } else { GenericArgPosition::Value }, - def.parent.is_none() && def.has_self, // `has_self` - seg.infer_args || suppress_mismatch, // `infer_args` + tcx, span, def_id, seg, generics, gen_args, gen_pos, has_self, infer_args, ) } @@ -365,156 +381,109 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pub(crate) fn check_generic_arg_count( tcx: TyCtxt<'_>, span: Span, - def: &ty::Generics, - args: &hir::GenericArgs<'_>, - position: GenericArgPosition, + def_id: DefId, + seg: &hir::PathSegment<'_>, + gen_params: &ty::Generics, + gen_args: &hir::GenericArgs<'_>, + gen_pos: GenericArgPosition, has_self: bool, infer_args: bool, ) -> GenericArgCountResult { - // At this stage we are guaranteed that the generic arguments are in the correct order, e.g. - // that lifetimes will proceed types. So it suffices to check the number of each generic - // arguments in order to validate them with respect to the generic parameters. - let param_counts = def.own_counts(); + let default_counts = gen_params.own_defaults(); + let param_counts = gen_params.own_counts(); let named_type_param_count = param_counts.types - has_self as usize; - let arg_counts = args.own_counts(); - let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; - - let mut defaults: ty::GenericParamCount = Default::default(); - for param in &def.params { - match param.kind { - GenericParamDefKind::Lifetime => {} - GenericParamDefKind::Type { has_default, .. } => { - defaults.types += has_default as usize - } - GenericParamDefKind::Const => { - // FIXME(const_generics:defaults) - } - }; - } + let arg_counts = gen_args.own_counts(); + let infer_lifetimes = gen_pos != GenericArgPosition::Type && arg_counts.lifetimes == 0; - if position != GenericArgPosition::Type && !args.bindings.is_empty() { - AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); + if gen_pos != GenericArgPosition::Type && !gen_args.bindings.is_empty() { + Self::prohibit_assoc_ty_binding(tcx, gen_args.bindings[0].span); } let explicit_late_bound = - Self::prohibit_explicit_late_bound_lifetimes(tcx, def, args, position); - - let check_kind_count = |kind, - required, - permitted, - provided, - offset, - unexpected_spans: &mut Vec, - silent| { - debug!( - "check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}", - kind, required, permitted, provided, offset - ); - // We enforce the following: `required` <= `provided` <= `permitted`. - // For kinds without defaults (e.g.., lifetimes), `required == permitted`. - // For other kinds (i.e., types), `permitted` may be greater than `required`. - if required <= provided && provided <= permitted { - return true; - } + Self::prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos); - if silent { - return false; - } + let mut invalid_args = vec![]; - // Unfortunately lifetime and type parameter mismatches are typically styled - // differently in diagnostics, which means we have a few cases to consider here. - let (bound, quantifier) = if required != permitted { - if provided < required { - (required, "at least ") - } else { - // provided > permitted - (permitted, "at most ") + let mut check_generics = + |kind, expected_min, expected_max, provided, params_offset, args_offset, silent| { + if (expected_min..=expected_max).contains(&provided) { + return true; } - } else { - (required, "") - }; - let (spans, labels) = if provided > permitted { - // In the case when the user has provided too many arguments, - // we want to point to the unexpected arguments. - let (spans, labels): (Vec, Vec) = args.args - [offset + permitted..offset + provided] - .iter() - .map(|arg| (arg.span(), format!("unexpected {} argument", arg.short_descr()))) - .unzip(); - unexpected_spans.extend(spans.clone()); - (spans, labels) - } else { - ( - vec![span], - vec![format!( - "expected {}{} {} argument{}", - quantifier, - bound, - kind, - pluralize!(bound), - )], - ) - }; + if silent { + return false; + } - let mut err = tcx.sess.struct_span_err_with_code( - spans.clone(), - &format!( - "wrong number of {} arguments: expected {}{}, found {}", - kind, quantifier, bound, provided, - ), - DiagnosticId::Error("E0107".into()), - ); - for (span, label) in spans.into_iter().zip(labels) { - err.span_label(span, label.as_str()); - } - err.emit(); - false - }; + if provided > expected_max { + invalid_args.extend( + gen_args.args[args_offset + expected_max..args_offset + provided] + .iter() + .map(|arg| arg.span()), + ); + }; + + WrongNumberOfGenericArgs { + tcx, + kind, + expected_min, + expected_max, + provided, + params_offset, + args_offset, + path_segment: seg, + gen_params, + gen_args, + def_id, + span, + } + .diagnostic() + .emit(); - let mut unexpected_spans = vec![]; + false + }; - let lifetime_count_correct = check_kind_count( + let lifetimes_correct = check_generics( "lifetime", if infer_lifetimes { 0 } else { param_counts.lifetimes }, param_counts.lifetimes, arg_counts.lifetimes, + has_self as usize, 0, - &mut unexpected_spans, explicit_late_bound == ExplicitLateBound::Yes, ); - let kind_str = if param_counts.consts + arg_counts.consts == 0 { - "type" - } else if named_type_param_count + arg_counts.types == 0 { - "const" - } else { - "generic" - }; + let args_correct = { + let kind = if param_counts.consts + arg_counts.consts == 0 { + "type" + } else if named_type_param_count + arg_counts.types == 0 { + "const" + } else { + "generic" + }; - let arg_count_correct = check_kind_count( - kind_str, - if infer_args { + let expected_min = if infer_args { 0 } else { - param_counts.consts + named_type_param_count - defaults.types - }, - param_counts.consts + named_type_param_count, - arg_counts.consts + arg_counts.types, - arg_counts.lifetimes, - &mut unexpected_spans, - false, - ); + param_counts.consts + named_type_param_count - default_counts.types + }; + + check_generics( + kind, + expected_min, + param_counts.consts + named_type_param_count, + arg_counts.consts + arg_counts.types, + param_counts.lifetimes + has_self as usize, + arg_counts.lifetimes, + false, + ) + }; GenericArgCountResult { explicit_late_bound, - correct: if lifetime_count_correct && arg_count_correct { + correct: if lifetimes_correct && args_correct { Ok(()) } else { - Err(GenericArgCountMismatch { - reported: Some(ErrorReported), - invalid_args: unexpected_spans, - }) + Err(GenericArgCountMismatch { reported: Some(ErrorReported), invalid_args }) }, } } @@ -526,22 +495,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generics: &ty::Generics, ) -> bool { let explicit = !seg.infer_args; - let impl_trait = - generics.params.iter().any(|param| match param.kind { + let impl_trait = generics.params.iter().any(|param| { + matches!( + param.kind, ty::GenericParamDefKind::Type { - synthetic: - Some( - hir::SyntheticTyParamKind::ImplTrait - | hir::SyntheticTyParamKind::FromAttr, - ), + synthetic: Some( + hir::SyntheticTyParamKind::ImplTrait | hir::SyntheticTyParamKind::FromAttr, + ), .. - } => true, - _ => false, - }); + } + ) + }); if explicit && impl_trait { let spans = seg - .generic_args() + .args() .args .iter() .filter_map(|arg| match arg { @@ -586,12 +554,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; if infer_lifetimes { - ExplicitLateBound::No - } else if let Some(span_late) = def.has_late_bound_regions { + return ExplicitLateBound::No; + } + + if let Some(span_late) = def.has_late_bound_regions { let msg = "cannot specify lifetime arguments explicitly \ if late bound lifetime parameters are present"; let note = "the late bound lifetime parameter is introduced here"; let span = args.args[0].span(); + if position == GenericArgPosition::Value && arg_counts.lifetimes != param_counts.lifetimes { @@ -608,6 +579,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { |lint| lint.build(msg).emit(), ); } + ExplicitLateBound::Yes } else { ExplicitLateBound::No diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 38d33e5586..5659345f0f 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -112,12 +112,15 @@ pub enum SizedByDefault { No, } +#[derive(Debug)] struct ConvertedBinding<'a, 'tcx> { item_name: Ident, kind: ConvertedBindingKind<'a, 'tcx>, + gen_args: &'a GenericArgs<'a>, span: Span, } +#[derive(Debug)] enum ConvertedBindingKind<'a, 'tcx> { Equality(Ty<'tcx>), Constraint(&'a [hir::GenericBound<'a>]), @@ -138,6 +141,12 @@ pub enum ExplicitLateBound { No, } +#[derive(Copy, Clone, PartialEq)] +pub enum IsMethodCall { + Yes, + No, +} + /// Denotes the "position" of a generic argument, indicating if it is a generic type, /// generic function or generic method call. #[derive(Copy, Clone, PartialEq)] @@ -252,7 +261,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, def_id, &[], - item_segment.generic_args(), + item_segment, + item_segment.args(), item_segment.infer_args, None, ); @@ -300,6 +310,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span: Span, def_id: DefId, parent_substs: &[subst::GenericArg<'tcx>], + seg: &hir::PathSegment<'_>, generic_args: &'a hir::GenericArgs<'_>, infer_args: bool, self_ty: Option>, @@ -314,10 +325,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); let tcx = self.tcx(); - let generic_params = tcx.generics_of(def_id); + let generics = tcx.generics_of(def_id); + debug!("generics: {:?}", generics); - if generic_params.has_self { - if generic_params.parent.is_some() { + if generics.has_self { + if generics.parent.is_some() { // The parent is a trait so it should have at least one subst // for the `Self` type. assert!(!parent_substs.is_empty()) @@ -332,7 +344,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let arg_count = Self::check_generic_arg_count( tcx, span, - &generic_params, + def_id, + seg, + &generics, &generic_args, GenericArgPosition::Type, self_ty.is_some(), @@ -343,7 +357,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Traits always have `Self` as a generic parameter, which means they will not return early // here and so associated type bindings will be handled regardless of whether there are any // non-`Self` generic parameters. - if generic_params.params.len() == 0 { + if generics.params.len() == 0 { return (tcx.intern_substs(&[]), vec![], arg_count); } @@ -486,7 +500,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } GenericParamDefKind::Const => { let ty = tcx.at(self.span).type_of(param.def_id); - // FIXME(const_generics:defaults) + // FIXME(const_generics_defaults) if infer_args { // No const parameters were provided, we can infer all. self.astconv.ct_infer(ty, Some(param), self.span).into() @@ -547,13 +561,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ConvertedBindingKind::Constraint(bounds) } }; - ConvertedBinding { item_name: binding.ident, kind, span: binding.span } + ConvertedBinding { + item_name: binding.ident, + kind, + gen_args: binding.gen_args, + span: binding.span, + } }) .collect(); debug!( "create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}", - generic_params, self_ty, substs + generics, self_ty, substs ); (substs, assoc_bindings, arg_count) @@ -576,7 +595,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, item_def_id, parent_substs, - item_segment.generic_args(), + item_segment, + item_segment.args(), item_segment.infer_args, None, ) @@ -701,8 +721,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) { let trait_def_id = self.tcx().require_lang_item(lang_item, Some(span)); - let (substs, assoc_bindings, _) = - self.create_substs_for_ast_path(span, trait_def_id, &[], args, false, Some(self_ty)); + let (substs, assoc_bindings, _) = self.create_substs_for_ast_path( + span, + trait_def_id, + &[], + &hir::PathSegment::invalid(), + args, + false, + Some(self_ty), + ); let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs)); bounds.trait_bounds.push((poly_trait_ref, span, Constness::NotConst)); @@ -750,7 +777,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, trait_def_id, &[], - trait_segment.generic_args(), + trait_segment, + trait_segment.args(), trait_segment.infer_args, Some(self_ty), ) @@ -770,7 +798,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Try to find an unbound in bounds. let mut unbound = None; for ab in ast_bounds { - if let &hir::GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = ab { + if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab { if unbound.is_none() { unbound = Some(&ptr.trait_ref); } else { @@ -899,60 +927,27 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { dup_bindings: &mut FxHashMap, path_span: Span, ) -> Result<(), ErrorReported> { - let tcx = self.tcx(); - - if !speculative { - // Given something like `U: SomeTrait`, we want to produce a - // predicate like `::T = X`. This is somewhat - // subtle in the event that `T` is defined in a supertrait of - // `SomeTrait`, because in that case we need to upcast. - // - // That is, consider this case: - // - // ``` - // trait SubTrait: SuperTrait { } - // trait SuperTrait { type T; } - // - // ... B: SubTrait ... - // ``` - // - // We want to produce `>::T == foo`. - - // Find any late-bound regions declared in `ty` that are not - // declared in the trait-ref. These are not well-formed. - // - // Example: - // - // for<'a> ::Item = &'a str // <-- 'a is bad - // for<'a> >::Output = &'a str // <-- 'a is ok - if let ConvertedBindingKind::Equality(ty) = binding.kind { - let late_bound_in_trait_ref = - tcx.collect_constrained_late_bound_regions(&trait_ref); - let late_bound_in_ty = - tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(ty)); - debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref); - debug!("late_bound_in_ty = {:?}", late_bound_in_ty); + // Given something like `U: SomeTrait`, we want to produce a + // predicate like `::T = X`. This is somewhat + // subtle in the event that `T` is defined in a supertrait of + // `SomeTrait`, because in that case we need to upcast. + // + // That is, consider this case: + // + // ``` + // trait SubTrait: SuperTrait { } + // trait SuperTrait { type T; } + // + // ... B: SubTrait ... + // ``` + // + // We want to produce `>::T == foo`. - // FIXME: point at the type params that don't have appropriate lifetimes: - // struct S1 Fn(&i32, &i32) -> &'a i32>(F); - // ---- ---- ^^^^^^^ - self.validate_late_bound_regions( - late_bound_in_trait_ref, - late_bound_in_ty, - |br_name| { - struct_span_err!( - tcx.sess, - binding.span, - E0582, - "binding for associated type `{}` references {}, \ - which does not appear in the trait input types", - binding.item_name, - br_name - ) - }, - ); - } - } + debug!( + "add_predicates_for_ast_type_binding(hir_ref_id {:?}, trait_ref {:?}, binding {:?}, bounds {:?}", + hir_ref_id, trait_ref, binding, bounds + ); + let tcx = self.tcx(); let candidate = if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) { @@ -1011,6 +1006,72 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .or_insert(binding.span); } + // Include substitutions for generic parameters of associated types + let projection_ty = candidate.map_bound(|trait_ref| { + let item_segment = hir::PathSegment { + ident: assoc_ty.ident, + hir_id: None, + res: None, + args: Some(binding.gen_args), + infer_args: false, + }; + + let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item( + tcx, + path_span, + assoc_ty.def_id, + &item_segment, + trait_ref.substs, + ); + + debug!( + "add_predicates_for_ast_type_binding: substs for trait-ref and assoc_item: {:?}", + substs_trait_ref_and_assoc_item + ); + + ty::ProjectionTy { + item_def_id: assoc_ty.def_id, + substs: substs_trait_ref_and_assoc_item, + } + }); + + if !speculative { + // Find any late-bound regions declared in `ty` that are not + // declared in the trait-ref or assoc_ty. These are not well-formed. + // + // Example: + // + // for<'a> ::Item = &'a str // <-- 'a is bad + // for<'a> >::Output = &'a str // <-- 'a is ok + if let ConvertedBindingKind::Equality(ty) = binding.kind { + let late_bound_in_trait_ref = + tcx.collect_constrained_late_bound_regions(&projection_ty); + let late_bound_in_ty = + tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(ty)); + debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref); + debug!("late_bound_in_ty = {:?}", late_bound_in_ty); + + // FIXME: point at the type params that don't have appropriate lifetimes: + // struct S1 Fn(&i32, &i32) -> &'a i32>(F); + // ---- ---- ^^^^^^^ + self.validate_late_bound_regions( + late_bound_in_trait_ref, + late_bound_in_ty, + |br_name| { + struct_span_err!( + tcx.sess, + binding.span, + E0582, + "binding for associated type `{}` references {}, \ + which does not appear in the trait input types", + binding.item_name, + br_name + ) + }, + ); + } + } + match binding.kind { ConvertedBindingKind::Equality(ref ty) => { // "Desugar" a constraint like `T: Iterator` this to @@ -1018,13 +1079,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // // `::Item = u32` bounds.projection_bounds.push(( - candidate.map_bound(|trait_ref| ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy::from_ref_and_name( - tcx, - trait_ref, - binding.item_name, - ), - ty, + projection_ty.map_bound(|projection_ty| { + debug!( + "add_predicates_for_ast_type_binding: projection_ty {:?}, substs: {:?}", + projection_ty, projection_ty.substs + ); + ty::ProjectionPredicate { projection_ty, ty } }), binding.span, )); @@ -1036,7 +1096,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty` // parameter to have a skipped binder. - let param_ty = tcx.mk_projection(assoc_ty.def_id, candidate.skip_binder().substs); + let param_ty = + tcx.mk_projection(assoc_ty.def_id, projection_ty.skip_binder().substs); self.add_bounds(param_ty, ast_bounds, bounds); } } @@ -1076,7 +1137,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { dummy_self, &mut bounds, ) { - potential_assoc_types.extend(cur_potential_assoc_types.into_iter()); + potential_assoc_types.extend(cur_potential_assoc_types); } } @@ -1158,9 +1219,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { obligation.predicate ); - let bound_predicate = obligation.predicate.bound_atom(); + let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(pred, _) => { + ty::PredicateKind::Trait(pred, _) => { let pred = bound_predicate.rebind(pred); associated_types.entry(span).or_default().extend( tcx.associated_items(pred.def_id()) @@ -1169,7 +1230,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .map(|item| item.def_id), ); } - ty::PredicateAtom::Projection(pred) => { + ty::PredicateKind::Projection(pred) => { let pred = bound_predicate.rebind(pred); // A `Self` within the original bound will be substituted with a // `trait_object_dummy_self`, so check for that. @@ -1256,17 +1317,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }) }); - let regular_trait_predicates = existential_trait_refs.map(|trait_ref| { - trait_ref.map_bound(|trait_ref| ty::ExistentialPredicate::Trait(trait_ref)) - }); + let regular_trait_predicates = existential_trait_refs + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)); let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| { ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id())) }); let mut v = regular_trait_predicates .chain(auto_trait_predicates) .chain( - existential_projections - .map(|x| x.map_bound(|x| ty::ExistentialPredicate::Projection(x))), + existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)), ) .collect::>(); v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); @@ -1753,7 +1812,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut has_err = false; for segment in segments { let (mut err_for_lt, mut err_for_ty, mut err_for_ct) = (false, false, false); - for arg in segment.generic_args().args { + for arg in segment.args().args { let (span, kind) = match arg { hir::GenericArg::Lifetime(lt) => { if err_for_lt { @@ -1795,7 +1854,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } // Only emit the first error to avoid overloading the user with error messages. - if let [binding, ..] = segment.generic_args().bindings { + if let [binding, ..] = segment.args().bindings { has_err = true; Self::prohibit_assoc_ty_binding(self.tcx(), binding.span); } @@ -2013,11 +2072,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { "generic `Self` types are currently not permitted in anonymous constants", ); if let Some(hir::Node::Item(&hir::Item { - kind: hir::ItemKind::Impl { self_ty, .. }, + kind: hir::ItemKind::Impl(ref impl_), .. })) = tcx.hir().get_if_local(def_id) { - err.span_note(self_ty.span, "not a concrete type"); + err.span_note(impl_.self_ty.span, "not a concrete type"); } err.emit(); tcx.ty_error() @@ -2042,9 +2101,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { match prim_ty { hir::PrimTy::Bool => tcx.types.bool, hir::PrimTy::Char => tcx.types.char, - hir::PrimTy::Int(it) => tcx.mk_mach_int(it), - hir::PrimTy::Uint(uit) => tcx.mk_mach_uint(uit), - hir::PrimTy::Float(ft) => tcx.mk_mach_float(ft), + hir::PrimTy::Int(it) => tcx.mk_mach_int(ty::int_ty(it)), + hir::PrimTy::Uint(uit) => tcx.mk_mach_uint(ty::uint_ty(uit)), + hir::PrimTy::Float(ft) => tcx.mk_mach_float(ty::float_ty(ft)), hir::PrimTy::Str => tcx.types.str_, } } @@ -2132,6 +2191,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, def_id, &[], + &hir::PathSegment::invalid(), &GenericArgs::none(), true, None, diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 6467e04407..30e0e3eecd 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -1,9 +1,9 @@ -use crate::check::coercion::CoerceMany; +use crate::check::coercion::{AsCoercionSite, CoerceMany}; use crate::check::{Diverges, Expectation, FnCtxt, Needs}; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::Obligation; -use rustc_middle::ty::{self, ToPredicate, Ty}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyS}; use rustc_span::Span; use rustc_trait_selection::opaque_types::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -12,6 +12,41 @@ use rustc_trait_selection::traits::{ StatementAsExpression, }; +macro_rules! create_maybe_get_coercion_reason { + ($fn_name:ident, $node:expr) => { + pub(crate) fn $fn_name(&self, hir_id: hir::HirId, sp: Span) -> Option<(Span, String)> { + let node = $node(self.tcx.hir(), hir_id); + if let hir::Node::Block(block) = node { + // check that the body's parent is an fn + let parent = self.tcx.hir().get( + self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(block.hir_id)), + ); + if let ( + Some(expr), + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }), + ) = (&block.expr, parent) + { + // check that the `if` expr without `else` is the fn body's expr + if expr.span == sp { + return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| { + let span = fn_decl.output.span(); + let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?; + Some(( + span, + format!("expected `{}` because of this return type", snippet), + )) + }); + } + } + } + if let hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) = node { + return Some((pat.span, "expected because of this assignment".to_string())); + } + None + } + }; +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_match( &self, @@ -25,7 +60,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { use hir::MatchSource::*; let (source_if, if_no_else, force_scrutinee_bool) = match match_src { - IfDesugar { contains_else_clause } => (true, !contains_else_clause, true), IfLetDesugar { contains_else_clause, .. } => (true, !contains_else_clause, false), WhileDesugar => (false, false, true), _ => (false, false, false), @@ -115,8 +149,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let arm_ty = if source_if && if_no_else && i != 0 - && self.if_fallback_coercion(expr.span, &arms[0].body, &mut coercion) - { + && self.if_fallback_coercion( + expr.span, + &arms[0].body, + &mut coercion, + |hir_id, span| self.maybe_get_coercion_reason(hir_id, span), + ) { tcx.ty_error() } else { // Only call this if this is not an `if` expr with an expected type and no `else` @@ -125,58 +163,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; all_arms_diverge &= self.diverges.get(); - // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` - // we check if the different arms would work with boxed trait objects instead and - // provide a structured suggestion in that case. - let opt_suggest_box_span = match ( - orig_expected, - self.ret_coercion_impl_trait.map(|ty| (self.body_id.owner, ty)), - ) { - (Expectation::ExpectHasType(expected), Some((id, ty))) - if self.in_tail_expr && self.can_coerce(arm_ty, expected) => - { - let impl_trait_ret_ty = self.infcx.instantiate_opaque_types( - id, - self.body_id, - self.param_env, - ty, - arm.body.span, - ); - let mut suggest_box = !impl_trait_ret_ty.obligations.is_empty(); - for o in impl_trait_ret_ty.obligations { - match o.predicate.skip_binders_unchecked() { - ty::PredicateAtom::Trait(t, constness) => { - let pred = ty::PredicateAtom::Trait( - ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: t.def_id(), - substs: self.infcx.tcx.mk_substs_trait(arm_ty, &[]), - }, - }, - constness, - ); - let obl = Obligation::new( - o.cause.clone(), - self.param_env, - pred.to_predicate(self.infcx.tcx), - ); - suggest_box &= self.infcx.predicate_must_hold_modulo_regions(&obl); - if !suggest_box { - // We've encountered some obligation that didn't hold, so the - // return expression can't just be boxed. We don't need to - // evaluate the rest of the obligations. - break; - } - } - _ => {} - } - } - // If all the obligations hold (or there are no obligations) the tail expression - // we can suggest to return a boxed trait object instead of an opaque type. - if suggest_box { self.ret_type_span } else { None } - } - _ => None, - }; + let opt_suggest_box_span = + self.opt_suggest_box_span(arm.body.span, arm_ty, orig_expected); if source_if { let then_expr = &arms[0].body; @@ -279,7 +267,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { use hir::MatchSource::*; let msg = match source { - IfDesugar { .. } | IfLetDesugar { .. } => "block in `if` expression", + IfLetDesugar { .. } => "block in `if` expression", WhileDesugar { .. } | WhileLetDesugar { .. } => "block in `while` expression", _ => "arm", }; @@ -291,15 +279,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Handle the fallback arm of a desugared if(-let) like a missing else. /// /// Returns `true` if there was an error forcing the coercion to the `()` type. - fn if_fallback_coercion( + pub(crate) fn if_fallback_coercion( &self, span: Span, then_expr: &'tcx hir::Expr<'tcx>, - coercion: &mut CoerceMany<'tcx, '_, rustc_hir::Arm<'tcx>>, - ) -> bool { + coercion: &mut CoerceMany<'tcx, '_, T>, + ret_reason: F, + ) -> bool + where + F: Fn(hir::HirId, Span) -> Option<(Span, String)>, + T: AsCoercionSite, + { // If this `if` expr is the parent's function return expr, // the cause of the type coercion is the return type, point at it. (#25228) - let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span); + let ret_reason = ret_reason(then_expr.hir_id, span); let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse); let mut error = false; coercion.coerce_forced_unit( @@ -322,38 +315,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error } - fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, span: Span) -> Option<(Span, String)> { - use hir::Node::{Block, Item, Local}; - - let hir = self.tcx.hir(); - let arm_id = hir.get_parent_node(hir_id); - let match_id = hir.get_parent_node(arm_id); - let containing_id = hir.get_parent_node(match_id); - - let node = hir.get(containing_id); - if let Block(block) = node { - // check that the body's parent is an fn - let parent = hir.get(hir.get_parent_node(hir.get_parent_node(block.hir_id))); - if let (Some(expr), Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) = - (&block.expr, parent) - { - // check that the `if` expr without `else` is the fn body's expr - if expr.span == span { - return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| { - let span = fn_decl.output.span(); - let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?; - Some((span, format!("expected `{}` because of this return type", snippet))) - }); - } - } + create_maybe_get_coercion_reason!( + maybe_get_coercion_reason, + |hir: rustc_middle::hir::map::Map<'a>, id| { + let arm_id = hir.get_parent_node(id); + let match_id = hir.get_parent_node(arm_id); + let containing_id = hir.get_parent_node(match_id); + hir.get(containing_id) } - if let Local(hir::Local { ty: Some(_), pat, .. }) = node { - return Some((pat.span, "expected because of this assignment".to_string())); + ); + + create_maybe_get_coercion_reason!( + maybe_get_coercion_reason_if, + |hir: rustc_middle::hir::map::Map<'a>, id| { + let rslt = hir.get_parent_node(hir.get_parent_node(id)); + hir.get(rslt) } - None - } + ); - fn if_cause( + pub(crate) fn if_cause( &self, span: Span, then_expr: &'tcx hir::Expr<'tcx>, @@ -546,6 +526,58 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (block.span, None) } } + + // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` + // we check if the different arms would work with boxed trait objects instead and + // provide a structured suggestion in that case. + pub(crate) fn opt_suggest_box_span( + &self, + span: Span, + outer_ty: &'tcx TyS<'tcx>, + orig_expected: Expectation<'tcx>, + ) -> Option { + match (orig_expected, self.ret_coercion_impl_trait.map(|ty| (self.body_id.owner, ty))) { + (Expectation::ExpectHasType(expected), Some((id, ty))) + if self.in_tail_expr && self.can_coerce(outer_ty, expected) => + { + let impl_trait_ret_ty = + self.infcx.instantiate_opaque_types(id, self.body_id, self.param_env, ty, span); + let mut suggest_box = !impl_trait_ret_ty.obligations.is_empty(); + for o in impl_trait_ret_ty.obligations { + match o.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(t, constness) => { + let pred = ty::PredicateKind::Trait( + ty::TraitPredicate { + trait_ref: ty::TraitRef { + def_id: t.def_id(), + substs: self.infcx.tcx.mk_substs_trait(outer_ty, &[]), + }, + }, + constness, + ); + let obl = Obligation::new( + o.cause.clone(), + self.param_env, + pred.to_predicate(self.infcx.tcx), + ); + suggest_box &= self.infcx.predicate_must_hold_modulo_regions(&obl); + if !suggest_box { + // We've encountered some obligation that didn't hold, so the + // return expression can't just be boxed. We don't need to + // evaluate the rest of the obligations. + break; + } + } + _ => {} + } + } + // If all the obligations hold (or there are no obligations) the tail expression + // we can suggest to return a boxed trait object instead of an opaque type. + if suggest_box { self.ret_type_span } else { None } + } + _ => None, + } + } } fn arms_contain_ref_bindings(arms: &'tcx [hir::Arm<'tcx>]) -> Option { diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 22e287320d..4836418b3c 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -25,24 +25,24 @@ pub fn check_legal_trait_for_method_call( tcx: TyCtxt<'_>, span: Span, receiver: Option, + expr_span: Span, trait_id: DefId, ) { if tcx.lang_items().drop_trait() == Some(trait_id) { let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method"); err.span_label(span, "explicit destructor calls not allowed"); - let snippet = receiver + let (sp, suggestion) = receiver .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok()) - .unwrap_or_default(); - - let suggestion = - if snippet.is_empty() { "drop".to_string() } else { format!("drop({})", snippet) }; + .filter(|snippet| !snippet.is_empty()) + .map(|snippet| (expr_span, format!("drop({})", snippet))) + .unwrap_or_else(|| (span, "drop".to_string())); err.span_suggestion( - span, - &format!("consider using `drop` function: `{}`", suggestion), - String::new(), - Applicability::Unspecified, + sp, + "consider using `drop` function", + suggestion, + Applicability::MaybeIncorrect, ); err.emit(); @@ -290,16 +290,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::FnPtr(sig) => (sig, None), ref t => { let mut unit_variant = None; - if let &ty::Adt(adt_def, ..) = t { + if let ty::Adt(adt_def, ..) = t { if adt_def.is_enum() { - if let hir::ExprKind::Call(ref expr, _) = call_expr.kind { + if let hir::ExprKind::Call(expr, _) = call_expr.kind { unit_variant = self.tcx.sess.source_map().span_to_snippet(expr.span).ok(); } } } - if let hir::ExprKind::Call(ref callee, _) = call_expr.kind { + if let hir::ExprKind::Call(callee, _) = call_expr.kind { let mut err = type_error_struct!( self.tcx.sess, callee.span, diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 36240a9b41..7924ffe8a6 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -32,7 +32,6 @@ use super::FnCtxt; use crate::hir::def_id::DefId; use crate::type_error_struct; -use rustc_ast as ast; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; @@ -418,13 +417,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.emit(); } CastError::SizedUnsizedCast => { - use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic}; - SizedUnsizedCastError::new( - &fcx.tcx.sess, - self.span, - self.expr_ty, - fcx.ty_to_string(self.cast_ty), - ) + use crate::structured_errors::{SizedUnsizedCast, StructuredDiagnostic}; + + SizedUnsizedCast { + sess: &fcx.tcx.sess, + span: self.span, + expr_ty: self.expr_ty, + cast_ty: fcx.ty_to_string(self.cast_ty), + } .diagnostic() .emit(); } @@ -659,7 +659,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { (_, Int(Bool)) => Err(CastError::CastToBool), // * -> Char - (Int(U(ast::UintTy::U8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast + (Int(U(ty::UintTy::U8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast (_, Int(Char)) => Err(CastError::CastToChar), // prim -> float,ptr diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index d235ad21c7..8e2b0bfd66 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -42,6 +42,17 @@ pub(super) fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) { ) .emit() } + + // This ABI is only allowed on function pointers + if abi == Abi::CCmseNonSecureCall { + struct_span_err!( + tcx.sess, + span, + E0781, + "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers." + ) + .emit() + } } /// Helper used for fns and closures. Does the grungy work of checking a function @@ -66,7 +77,7 @@ pub(super) fn check_fn<'a, 'tcx>( // 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); - *fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id); + fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id)); let tcx = fcx.tcx; let sess = tcx.sess; @@ -103,13 +114,17 @@ pub(super) fn check_fn<'a, 'tcx>( Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(header, ..), .. }) => Some(header), + Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(header, ..), + .. + }) => Some(header), // Closures are RustCall, but they tuple their arguments, so shouldn't be checked Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => None, node => bug!("Item being checked wasn't a function/closure: {:?}", node), }; if let Some(header) = item { - tcx.sess.span_err(header.span, "A function with the \"rust-call\" ABI must take a single non-self argument that is a tuple") + tcx.sess.span_err(header.span, "functions with the \"rust-call\" ABI must take a single non-self argument that is a tuple") } }; @@ -542,10 +557,9 @@ pub(super) fn check_opaque_for_inheriting_lifetimes( if let Some(ty) = prohibit_opaque.break_value() { let is_async = match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin { - hir::OpaqueTyOrigin::AsyncFn => true, - _ => false, - }, + ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { + matches!(origin, hir::OpaqueTyOrigin::AsyncFn) + } _ => unreachable!(), }; @@ -688,11 +702,17 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { check_enum(tcx, it.span, &enum_definition.variants, it.hir_id); } hir::ItemKind::Fn(..) => {} // entirely within check_item_body - hir::ItemKind::Impl { ref items, .. } => { + hir::ItemKind::Impl(ref impl_) => { debug!("ItemKind::Impl {} with id {}", it.ident, it.hir_id); let impl_def_id = tcx.hir().local_def_id(it.hir_id); if let Some(impl_trait_ref) = tcx.impl_trait_ref(impl_def_id) { - check_impl_items_against_trait(tcx, it.span, impl_def_id, impl_trait_ref, items); + check_impl_items_against_trait( + tcx, + it.span, + impl_def_id, + impl_trait_ref, + &impl_.items, + ); let trait_def_id = impl_trait_ref.def_id; check_on_unimplemented(tcx, trait_def_id, it); } @@ -836,21 +856,13 @@ pub(super) fn check_specialization_validity<'tcx>( Ok(ancestors) => ancestors, Err(_) => return, }; - let mut ancestor_impls = ancestors - .skip(1) - .filter_map(|parent| { - if parent.is_from_trait() { - None - } else { - Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id))) - } - }) - .peekable(); - - if ancestor_impls.peek().is_none() { - // No parent, nothing to specialize. - return; - } + let mut ancestor_impls = ancestors.skip(1).filter_map(|parent| { + if parent.is_from_trait() { + None + } else { + Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id))) + } + }); let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| { match parent_item { @@ -892,8 +904,6 @@ pub(super) fn check_impl_items_against_trait<'tcx>( impl_trait_ref: ty::TraitRef<'tcx>, impl_item_refs: &[hir::ImplItemRef<'_>], ) { - let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); - // If the trait reference itself is erroneous (so the compilation is going // to fail), skip checking the items here -- the `impl_item` table in `tcx` // isn't populated for such impls. @@ -921,111 +931,75 @@ pub(super) fn check_impl_items_against_trait<'tcx>( // Locate trait definition and items let trait_def = tcx.trait_def(impl_trait_ref.def_id); - - let impl_items = || impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id)); + let impl_items = impl_item_refs.iter().map(|iiref| tcx.hir().impl_item(iiref.id)); + let associated_items = tcx.associated_items(impl_trait_ref.def_id); // Check existing impl methods to see if they are both present in trait // and compatible with trait signature - for impl_item in impl_items() { - let namespace = impl_item.kind.namespace(); + for impl_item in impl_items { let ty_impl_item = tcx.associated_item(tcx.hir().local_def_id(impl_item.hir_id)); - let ty_trait_item = tcx - .associated_items(impl_trait_ref.def_id) - .find_by_name_and_namespace(tcx, ty_impl_item.ident, namespace, impl_trait_ref.def_id) - .or_else(|| { - // Not compatible, but needed for the error message - tcx.associated_items(impl_trait_ref.def_id) - .filter_by_name(tcx, ty_impl_item.ident, impl_trait_ref.def_id) - .next() - }); - - // Check that impl definition matches trait definition - if let Some(ty_trait_item) = ty_trait_item { + + let mut items = + associated_items.filter_by_name(tcx, ty_impl_item.ident, impl_trait_ref.def_id); + + let (compatible_kind, ty_trait_item) = if let Some(ty_trait_item) = items.next() { + let is_compatible = |ty: &&ty::AssocItem| match (ty.kind, &impl_item.kind) { + (ty::AssocKind::Const, hir::ImplItemKind::Const(..)) => true, + (ty::AssocKind::Fn, hir::ImplItemKind::Fn(..)) => true, + (ty::AssocKind::Type, hir::ImplItemKind::TyAlias(..)) => true, + _ => false, + }; + + // If we don't have a compatible item, we'll use the first one whose name matches + // to report an error. + let mut compatible_kind = is_compatible(&ty_trait_item); + let mut trait_item = ty_trait_item; + + if !compatible_kind { + if let Some(ty_trait_item) = items.find(is_compatible) { + compatible_kind = true; + trait_item = ty_trait_item; + } + } + + (compatible_kind, trait_item) + } else { + continue; + }; + + if compatible_kind { match impl_item.kind { hir::ImplItemKind::Const(..) => { // Find associated const definition. - if ty_trait_item.kind == ty::AssocKind::Const { - compare_const_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - ); - } else { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0323, - "item `{}` is an associated const, \ - which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ); - err.span_label(impl_item.span, "does not match trait"); - // We can only get the spans from local trait definition - // Same for E0324 and E0325 - if let Some(trait_span) = tcx.hir().span_if_local(ty_trait_item.def_id) { - err.span_label(trait_span, "item in trait"); - } - err.emit() - } + compare_const_impl( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + ); } hir::ImplItemKind::Fn(..) => { let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - if ty_trait_item.kind == ty::AssocKind::Fn { - compare_impl_method( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } else { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0324, - "item `{}` is an associated method, \ - which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ); - err.span_label(impl_item.span, "does not match trait"); - if let Some(trait_span) = opt_trait_span { - err.span_label(trait_span, "item in trait"); - } - err.emit() - } + compare_impl_method( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); } hir::ImplItemKind::TyAlias(_) => { let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - if ty_trait_item.kind == ty::AssocKind::Type { - compare_ty_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } else { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0325, - "item `{}` is an associated type, \ - which doesn't match its trait `{}`", - ty_impl_item.ident, - impl_trait_ref.print_only_trait_path() - ); - err.span_label(impl_item.span, "does not match trait"); - if let Some(trait_span) = opt_trait_span { - err.span_label(trait_span, "item in trait"); - } - err.emit() - } + compare_ty_impl( + tcx, + &ty_impl_item, + impl_item.span, + &ty_trait_item, + impl_trait_ref, + opt_trait_span, + ); } } @@ -1036,12 +1010,22 @@ pub(super) fn check_impl_items_against_trait<'tcx>( impl_id.to_def_id(), impl_item, ); + } else { + report_mismatch_error( + tcx, + ty_trait_item.def_id, + impl_trait_ref, + impl_item, + &ty_impl_item, + ); } } - // Check for missing items from trait - let mut missing_items = Vec::new(); if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { + let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span); + + // Check for missing items from trait + let mut missing_items = Vec::new(); for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { let is_implemented = ancestors .leaf_def(tcx, trait_item.ident, trait_item.kind) @@ -1054,11 +1038,63 @@ pub(super) fn check_impl_items_against_trait<'tcx>( } } } + + if !missing_items.is_empty() { + missing_items_err(tcx, impl_span, &missing_items, full_impl_span); + } } +} - if !missing_items.is_empty() { - missing_items_err(tcx, impl_span, &missing_items, full_impl_span); +#[inline(never)] +#[cold] +fn report_mismatch_error<'tcx>( + tcx: TyCtxt<'tcx>, + trait_item_def_id: DefId, + impl_trait_ref: ty::TraitRef<'tcx>, + impl_item: &hir::ImplItem<'_>, + ty_impl_item: &ty::AssocItem, +) { + let mut err = match impl_item.kind { + hir::ImplItemKind::Const(..) => { + // Find associated const definition. + struct_span_err!( + tcx.sess, + impl_item.span, + E0323, + "item `{}` is an associated const, which doesn't match its trait `{}`", + ty_impl_item.ident, + impl_trait_ref.print_only_trait_path() + ) + } + + hir::ImplItemKind::Fn(..) => { + struct_span_err!( + tcx.sess, + impl_item.span, + E0324, + "item `{}` is an associated method, which doesn't match its trait `{}`", + ty_impl_item.ident, + impl_trait_ref.print_only_trait_path() + ) + } + + hir::ImplItemKind::TyAlias(_) => { + struct_span_err!( + tcx.sess, + impl_item.span, + E0325, + "item `{}` is an associated type, which doesn't match its trait `{}`", + ty_impl_item.ident, + impl_trait_ref.print_only_trait_path() + ) + } + }; + + err.span_label(impl_item.span, "does not match trait"); + if let Some(trait_span) = tcx.hir().span_if_local(trait_item_def_id) { + err.span_label(trait_span, "item in trait"); } + err.emit(); } /// Checks whether a type can be represented in memory. In particular, it @@ -1249,8 +1285,8 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: &'tcx ty let layout = tcx.layout_of(param_env.and(ty)); // We are currently checking the type this field came from, so it must be local let span = tcx.hir().span_if_local(field.did).unwrap(); - let zst = layout.map(|layout| layout.is_zst()).unwrap_or(false); - let align1 = layout.map(|layout| layout.align.abi.bytes() == 1).unwrap_or(false); + let zst = layout.map_or(false, |layout| layout.is_zst()); + let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1); (span, zst, align1) }); @@ -1320,10 +1356,7 @@ pub fn check_enum<'tcx>( } if tcx.adt_def(def_id).repr.int.is_none() && tcx.features().arbitrary_enum_discriminant { - let is_unit = |var: &hir::Variant<'_>| match var.data { - hir::VariantData::Unit(..) => true, - _ => false, - }; + let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..)); let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); let has_non_units = vs.iter().any(|var| !is_unit(var)); diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 7470c1a76a..f34aaec10a 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -192,9 +192,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { obligation.predicate ); - let bound_predicate = obligation.predicate.bound_atom(); - if let ty::PredicateAtom::Projection(proj_predicate) = - obligation.predicate.skip_binders() + let bound_predicate = obligation.predicate.kind(); + if let ty::PredicateKind::Projection(proj_predicate) = + obligation.predicate.kind().skip_binder() { // Given a Projection predicate, we can potentially infer // the complete signature. @@ -622,8 +622,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // where R is the return type we are expecting. This type `T` // will be our output. let output_ty = self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| { - let bound_predicate = obligation.predicate.bound_atom(); - if let ty::PredicateAtom::Projection(proj_predicate) = bound_predicate.skip_binder() { + let bound_predicate = obligation.predicate.kind(); + if let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder() { self.deduce_future_output_from_projection( obligation.cause.span, bound_predicate.rebind(proj_predicate), diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 0f5f0ab026..b2395b7bb2 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -583,9 +583,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { while !queue.is_empty() { let obligation = queue.remove(0); debug!("coerce_unsized resolve step: {:?}", obligation); - let bound_predicate = obligation.predicate.bound_atom(); + let bound_predicate = obligation.predicate.kind(); let trait_pred = match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(trait_pred, _) + ty::PredicateKind::Trait(trait_pred, _) if traits.contains(&trait_pred.def_id()) => { if unsize_did == trait_pred.def_id() { @@ -926,11 +926,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } }; - if is_capturing_closure(&prev_ty.kind()) || is_capturing_closure(&new_ty.kind()) { + if is_capturing_closure(prev_ty.kind()) || is_capturing_closure(new_ty.kind()) { (None, None) } else { - match (&prev_ty.kind(), &new_ty.kind()) { - (&ty::FnDef(..), &ty::FnDef(..)) => { + match (prev_ty.kind(), new_ty.kind()) { + (ty::FnDef(..), ty::FnDef(..)) => { // Don't reify if the function types have a LUB, i.e., they // are the same function and their parameters have a LUB. match self @@ -943,21 +943,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - (&ty::Closure(_, substs), &ty::FnDef(..)) => { + (ty::Closure(_, substs), ty::FnDef(..)) => { let b_sig = new_ty.fn_sig(self.tcx); let a_sig = self .tcx .signature_unclosure(substs.as_closure().sig(), b_sig.unsafety()); (Some(a_sig), Some(b_sig)) } - (&ty::FnDef(..), &ty::Closure(_, substs)) => { + (ty::FnDef(..), ty::Closure(_, substs)) => { let a_sig = prev_ty.fn_sig(self.tcx); let b_sig = self .tcx .signature_unclosure(substs.as_closure().sig(), a_sig.unsafety()); (Some(a_sig), Some(b_sig)) } - (&ty::Closure(_, substs_a), &ty::Closure(_, substs_b)) => ( + (ty::Closure(_, substs_a), ty::Closure(_, substs_b)) => ( Some(self.tcx.signature_unclosure( substs_a.as_closure().sig(), hir::Unsafety::Normal, @@ -1443,14 +1443,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { &mut err, expr, expected, found, cause.span, blk_id, ); let parent = fcx.tcx.hir().get(parent_id); - if let (Some(match_expr), true, false) = ( - fcx.tcx.hir().get_match_if_cause(expr.hir_id), + if let (Some(cond_expr), true, false) = ( + fcx.tcx.hir().get_if_cause(expr.hir_id), expected.is_unit(), pointing_at_return_type, ) { - if match_expr.span.desugaring_kind().is_none() { - err.span_label(match_expr.span, "expected this to be `()`"); - fcx.suggest_semicolon_at_end(match_expr.span, &mut err); + if cond_expr.span.desugaring_kind().is_none() { + err.span_label(cond_expr.span, "expected this to be `()`"); + fcx.suggest_semicolon_at_end(cond_expr.span, &mut err); } } fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) @@ -1472,22 +1472,22 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fn_output = Some(&fn_decl.output); // `impl Trait` return type } } - if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.borrow().as_ref(), fn_output) { - self.add_impl_trait_explanation(&mut err, cause, fcx, expected, *sp, fn_output); + if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) { + self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output); } - if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() { + if let Some(sp) = fcx.ret_coercion_span.get() { // If the closure has an explicit return type annotation, // then a type error may occur at the first return expression we // see in the closure (if it conflicts with the declared // return type). Skip adding a note in this case, since it // would be incorrect. - if !err.span.primary_spans().iter().any(|span| span == sp) { + if !err.span.primary_spans().iter().any(|&span| span == sp) { let hir = fcx.tcx.hir(); let body_owner = hir.body_owned_by(hir.enclosing_body_owner(fcx.body_id)); if fcx.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) { err.span_note( - *sp, + sp, &format!( "return type inferred to be `{}` here", fcx.resolve_vars_if_possible(expected) diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index bb324d0d8b..d37d6bc4f2 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -296,7 +296,7 @@ fn compare_predicate_entailment<'tcx>( { diag.span_suggestion( impl_err_span, - "consider change the type to match the mutability in trait", + "consider changing the mutability to match the trait", trait_err_str, Applicability::MachineApplicable, ); @@ -364,13 +364,12 @@ fn check_region_bounds_on_impl_item<'tcx>( if trait_params != impl_params { let item_kind = assoc_item_kind_str(impl_m); let def_span = tcx.sess.source_map().guess_head_span(span); - let span = tcx.hir().get_generics(impl_m.def_id).map(|g| g.span).unwrap_or(def_span); - let generics_span = if let Some(sp) = tcx.hir().span_if_local(trait_m.def_id) { + let span = tcx.hir().get_generics(impl_m.def_id).map_or(def_span, |g| g.span); + let generics_span = tcx.hir().span_if_local(trait_m.def_id).map(|sp| { let def_sp = tcx.sess.source_map().guess_head_span(sp); - Some(tcx.hir().get_generics(trait_m.def_id).map(|g| g.span).unwrap_or(def_sp)) - } else { - None - }; + tcx.hir().get_generics(trait_m.def_id).map_or(def_sp, |g| g.span) + }); + tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { span, item_kind, diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 2728e03171..3c9c683f4b 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -32,6 +32,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { return; } + self.suggest_no_capture_closure(err, expected, expr_ty); self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); self.suggest_missing_parentheses(err, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); @@ -815,6 +816,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |err: &mut DiagnosticBuilder<'_>, found_to_exp_is_fallible: bool, exp_to_found_is_fallible: bool| { + let exp_is_lhs = + expected_ty_expr.map(|e| self.tcx.hir().is_lhs(e.hir_id)).unwrap_or(false); + + if exp_is_lhs { + return; + } + let always_fallible = found_to_exp_is_fallible && (exp_to_found_is_fallible || expected_ty_expr.is_none()); let msg = if literal_is_ty_suffixed(expr) { diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index ad675f1e38..4c3c4fd447 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -226,13 +226,13 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // could be extended easily also to the other `Predicate`. let predicate_matches_closure = |p: Predicate<'tcx>| { let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env); - let predicate = predicate.bound_atom(); - let p = p.bound_atom(); + let predicate = predicate.kind(); + let p = p.kind(); match (predicate.skip_binder(), p.skip_binder()) { - (ty::PredicateAtom::Trait(a, _), ty::PredicateAtom::Trait(b, _)) => { + (ty::PredicateKind::Trait(a, _), ty::PredicateKind::Trait(b, _)) => { relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() } - (ty::PredicateAtom::Projection(a), ty::PredicateAtom::Projection(b)) => { + (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() } _ => predicate == p, @@ -267,15 +267,13 @@ crate fn check_drop_obligations<'a, 'tcx>( ty: Ty<'tcx>, span: Span, body_id: hir::HirId, -) -> Result<(), ErrorReported> { +) { debug!("check_drop_obligations typ: {:?}", ty); let cause = &ObligationCause::misc(span, body_id); let infer_ok = rcx.infcx.at(cause, rcx.fcx.param_env).dropck_outlives(ty); debug!("dropck_outlives = {:#?}", infer_ok); rcx.fcx.register_infer_ok_obligations(infer_ok); - - Ok(()) } // This is an implementation of the TypeRelation trait with the diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index ec0e039b5d..33b1c0bb2c 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -10,6 +10,7 @@ use crate::check::method::{probe, MethodError, SelfSource}; use crate::check::report_unexpected_variant_res; use crate::check::BreakableCtxt; use crate::check::Diverges; +use crate::check::DynamicCoerceMany; use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; use crate::check::FnCtxt; use crate::check::Needs; @@ -35,17 +36,17 @@ use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; +use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::Ty; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{AdtKind, Visibility}; +use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_trait_selection::traits::{self, ObligationCauseCode}; -use std::fmt::Display; - impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) { let ty = self.check_expr_with_hint(expr, expected); @@ -187,7 +188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Warn for non-block expressions with diverging children. match expr.kind { - ExprKind::Block(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {} + ExprKind::Block(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {} // If `expr` is a result of desugaring the try block and is an ok-wrapped // diverging expression (e.g. it arose from desugaring of `try { return }`), // we skip issuing a warning because it is autogenerated code. @@ -264,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), - ExprKind::Loop(ref body, _, source) => { + ExprKind::Loop(ref body, _, source, _) => { self.check_expr_loop(body, source, expected, expr) } ExprKind::Match(ref discrim, ref arms, match_src) => { @@ -284,6 +285,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_eq_type(&e, ty); ty } + ExprKind::If(ref cond, ref then_expr, ref opt_else_expr) => self.check_then_else( + &cond, + then_expr, + opt_else_expr.as_ref().map(|e| &**e), + expr.span, + expected, + ), ExprKind::DropTemps(ref e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(ref args) => self.check_expr_array(args, expected, expr), ExprKind::ConstBlock(ref anon_const) => self.to_const(anon_const).ty, @@ -671,14 +679,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.ret_coercion.is_none() { self.tcx.sess.emit_err(ReturnStmtOutsideOfFnBody { span: expr.span }); } else if let Some(ref e) = expr_opt { - if self.ret_coercion_span.borrow().is_none() { - *self.ret_coercion_span.borrow_mut() = Some(e.span); + if self.ret_coercion_span.get().is_none() { + self.ret_coercion_span.set(Some(e.span)); } self.check_return_expr(e); } else { let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); - if self.ret_coercion_span.borrow().is_none() { - *self.ret_coercion_span.borrow_mut() = Some(expr.span); + if self.ret_coercion_span.get().is_none() { + self.ret_coercion_span.set(Some(expr.span)); } let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression); if let Some((fn_decl, _)) = self.get_fn_decl(expr.hir_id) { @@ -738,8 +746,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } + // A generic function for checking the 'then' and 'else' clauses in an 'if' + // or 'if-else' expression. + fn check_then_else( + &self, + cond_expr: &'tcx hir::Expr<'tcx>, + then_expr: &'tcx hir::Expr<'tcx>, + opt_else_expr: Option<&'tcx hir::Expr<'tcx>>, + sp: Span, + orig_expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool, |_| {}); + + self.warn_if_unreachable(cond_expr.hir_id, then_expr.span, "block in `if` expression"); + + let cond_diverges = self.diverges.get(); + self.diverges.set(Diverges::Maybe); + + let expected = orig_expected.adjust_for_branches(self); + let then_ty = self.check_expr_with_expectation(then_expr, expected); + let then_diverges = self.diverges.get(); + self.diverges.set(Diverges::Maybe); + + // We've already taken the expected type's preferences + // into account when typing the `then` branch. To figure + // out the initial shot at a LUB, we thus only consider + // `expected` if it represents a *hard* constraint + // (`only_has_type`); otherwise, we just go with a + // fresh type variable. + let coerce_to_ty = expected.coercion_target_type(self, sp); + let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty); + + coerce.coerce(self, &self.misc(sp), then_expr, then_ty); + + if let Some(else_expr) = opt_else_expr { + let else_ty = self.check_expr_with_expectation(else_expr, expected); + let else_diverges = self.diverges.get(); + + let opt_suggest_box_span = + self.opt_suggest_box_span(else_expr.span, else_ty, orig_expected); + let if_cause = + self.if_cause(sp, then_expr, else_expr, then_ty, else_ty, opt_suggest_box_span); + + coerce.coerce(self, &if_cause, else_expr, else_ty); + + // We won't diverge unless both branches do (or the condition does). + self.diverges.set(cond_diverges | then_diverges & else_diverges); + } else { + self.if_fallback_coercion(sp, then_expr, &mut coerce, |hir_id, span| { + self.maybe_get_coercion_reason_if(hir_id, span) + }); + + // If the condition is false we can't diverge. + self.diverges.set(cond_diverges); + } + + let result_ty = coerce.complete(self); + if cond_ty.references_error() { self.tcx.ty_error() } else { result_ty } + } + /// Type check assignment expression `expr` of form `lhs = rhs`. - /// The expected type is `()` and is passsed to the function for the purposes of diagnostics. + /// The expected type is `()` and is passed to the function for the purposes of diagnostics. fn check_expr_assign( &self, expr: &'tcx hir::Expr<'tcx>, @@ -764,17 +831,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if !lhs.is_syntactic_place_expr() { // Do not suggest `if let x = y` as `==` is way more likely to be the intention. - if let hir::Node::Expr(hir::Expr { - kind: - ExprKind::Match( - _, - _, - hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar, - ), - .. - }) = self.tcx.hir().get( - self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(expr.hir_id)), - ) { + let mut span_err = || { // Likely `if let` intended. err.span_suggestion_verbose( expr.span.shrink_to_lo(), @@ -782,6 +839,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "let ".to_string(), applicability, ); + }; + if let hir::Node::Expr(hir::Expr { + kind: ExprKind::Match(_, _, hir::MatchSource::WhileDesugar), + .. + }) = self.tcx.hir().get( + self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(expr.hir_id)), + ) { + span_err(); + } else if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = + self.tcx.hir().get(self.tcx.hir().get_parent_node(expr.hir_id)) + { + span_err(); } } if eq { @@ -883,7 +952,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(method) } Err(error) => { - if segment.ident.name != kw::Invalid { + if segment.ident.name != kw::Empty { self.report_extended_method_error(segment, span, args, rcvr_t, error); } Err(()) @@ -1124,7 +1193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields, base_expr.is_none(), ); - if let &Some(ref base_expr) = base_expr { + if let Some(base_expr) = base_expr { // If check_expr_struct_fields hit an error, do not attempt to populate // the fields with the base_expr. This could cause us to hit errors later // when certain fields are assumed to exist that in fact do not. @@ -1181,8 +1250,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // re-link the regions that EIfEO can erase. self.demand_eqtype(span, adt_ty_hint, adt_ty); - let (substs, adt_kind, kind_name) = match &adt_ty.kind() { - &ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()), + let (substs, adt_kind, kind_name) = match adt_ty.kind() { + ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()), _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields"), }; @@ -1380,19 +1449,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty, ); match variant.ctor_kind { - CtorKind::Fn => { - err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty)); - err.span_label(field.ident.span, "field does not exist"); - err.span_label( - ty_span, - format!( - "`{adt}` is a tuple {kind_name}, \ - use the appropriate syntax: `{adt}(/* fields */)`", - adt = ty, - kind_name = kind_name - ), - ); - } + CtorKind::Fn => match ty.kind() { + ty::Adt(adt, ..) if adt.is_enum() => { + err.span_label( + variant.ident.span, + format!( + "`{adt}::{variant}` defined here", + adt = ty, + variant = variant.ident, + ), + ); + err.span_label(field.ident.span, "field does not exist"); + err.span_label( + ty_span, + format!( + "`{adt}::{variant}` is a tuple {kind_name}, \ + use the appropriate syntax: `{adt}::{variant}(/* fields */)`", + adt = ty, + variant = variant.ident, + kind_name = kind_name + ), + ); + } + _ => { + err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty)); + err.span_label(field.ident.span, "field does not exist"); + err.span_label( + ty_span, + format!( + "`{adt}` is a tuple {kind_name}, \ + use the appropriate syntax: `{adt}(/* fields */)`", + adt = ty, + kind_name = kind_name + ), + ); + } + }, _ => { // prevent all specified fields from being suggested let skip_fields = skip_fields.iter().map(|ref x| x.ident.name); @@ -1492,11 +1584,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, field: Ident, ) -> Ty<'tcx> { + debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); let expr_t = self.check_expr(base); let expr_t = self.structurally_resolved_type(base.span, expr_t); let mut private_candidate = None; let mut autoderef = self.autoderef(expr.span, expr_t); while let Some((base_t, _)) = autoderef.next() { + debug!("base_t: {:?}", base_t); match base_t.kind() { ty::Adt(base_def, substs) if !base_def.is_enum() => { debug!("struct named {:?}", base_t); @@ -1547,7 +1641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return field_ty; } - if field.name == kw::Invalid { + if field.name == kw::Empty { } else if self.method_exists(field, expr_t, expr.hir_id, true) { self.ban_take_value_of_method(expr, expr_t, field); } else if !expr_t.is_primitive_ty() { @@ -1613,7 +1707,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}", field, base, expr, expr_t ); - let mut err = self.no_such_field_err(field.span, field, expr_t); + let mut err = self.no_such_field_err(field, expr_t); match *expr_t.peel_refs().kind() { ty::Array(_, len) => { @@ -1637,8 +1731,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if field.name == kw::Await { // We know by construction that `.await` is either on Rust 2015 // or results in `ExprKind::Await`. Suggest switching the edition to 2018. - err.note("to `.await` a `Future`, switch to Rust 2018"); - err.help("set `edition = \"2018\"` in `Cargo.toml`"); + err.note("to `.await` a `Future`, switch to Rust 2018 or later"); + err.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)); err.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); } @@ -1787,21 +1881,120 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn no_such_field_err( + fn no_such_field_err( &self, - span: Span, - field: T, - expr_t: &ty::TyS<'_>, + field: Ident, + expr_t: &'tcx ty::TyS<'tcx>, ) -> DiagnosticBuilder<'_> { - type_error_struct!( + let span = field.span; + debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t); + + let mut err = type_error_struct!( self.tcx().sess, - span, + field.span, expr_t, E0609, "no field `{}` on type `{}`", field, expr_t - ) + ); + + // try to add a suggestion in case the field is a nested field of a field of the Adt + if let Some((fields, substs)) = self.get_field_candidates(span, &expr_t) { + for candidate_field in fields.iter() { + if let Some(field_path) = + self.check_for_nested_field(span, field, candidate_field, substs, vec![]) + { + let field_path_str = field_path + .iter() + .map(|id| id.name.to_ident_string()) + .collect::>() + .join("."); + debug!("field_path_str: {:?}", field_path_str); + + err.span_suggestion_verbose( + field.span.shrink_to_lo(), + "one of the expressions' fields has a field of the same name", + format!("{}.", field_path_str), + Applicability::MaybeIncorrect, + ); + } + } + } + err + } + + fn get_field_candidates( + &self, + span: Span, + base_t: Ty<'tcx>, + ) -> Option<(&Vec, SubstsRef<'tcx>)> { + debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t); + + let mut autoderef = self.autoderef(span, base_t); + while let Some((base_t, _)) = autoderef.next() { + match base_t.kind() { + ty::Adt(base_def, substs) if !base_def.is_enum() => { + let fields = &base_def.non_enum_variant().fields; + // For compile-time reasons put a limit on number of fields we search + if fields.len() > 100 { + return None; + } + return Some((fields, substs)); + } + _ => {} + } + } + None + } + + /// This method is called after we have encountered a missing field error to recursively + /// search for the field + fn check_for_nested_field( + &self, + span: Span, + target_field: Ident, + candidate_field: &ty::FieldDef, + subst: SubstsRef<'tcx>, + mut field_path: Vec, + ) -> Option> { + debug!( + "check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}", + span, candidate_field, field_path + ); + + if candidate_field.ident == target_field { + Some(field_path) + } else if field_path.len() > 3 { + // For compile-time reasons and to avoid infinite recursion we only check for fields + // up to a depth of three + None + } else { + // recursively search fields of `candidate_field` if it's a ty::Adt + + field_path.push(candidate_field.ident.normalize_to_macros_2_0()); + let field_ty = candidate_field.ty(self.tcx, subst); + if let Some((nested_fields, subst)) = self.get_field_candidates(span, &field_ty) { + for field in nested_fields.iter() { + let ident = field.ident.normalize_to_macros_2_0(); + if ident == target_field { + return Some(field_path); + } else { + let field_path = field_path.clone(); + if let Some(path) = self.check_for_nested_field( + span, + target_field, + field, + subst, + field_path, + ) { + return Some(path); + } + } + } + } + None + } } fn check_expr_index( diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 41a403a010..bc1a07801a 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -1,6 +1,6 @@ use crate::astconv::{ AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, - GenericArgCountResult, PathSeg, + GenericArgCountResult, IsMethodCall, PathSeg, }; use crate::check::callee::{self, DeferredCallResolution}; use crate::check::method::{self, MethodCallee, SelfSource}; @@ -274,10 +274,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let autoborrow_mut = adj.iter().any(|adj| { - matches!(adj, &Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), - .. - }) + matches!( + adj, + &Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), + .. + } + ) }); match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) { @@ -542,7 +545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicate(traits::Obligation::new( cause, self.param_env, - ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx), + ty::PredicateKind::WellFormed(arg).to_predicate(self.tcx), )); } @@ -764,21 +767,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .pending_obligations() .into_iter() .filter_map(move |obligation| { - let bound_predicate = obligation.predicate.bound_atom(); + let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateAtom::Projection(data) => { + ty::PredicateKind::Projection(data) => { Some((bound_predicate.rebind(data).to_poly_trait_ref(self.tcx), obligation)) } - ty::PredicateAtom::Trait(data, _) => { + ty::PredicateKind::Trait(data, _) => { Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation)) } - ty::PredicateAtom::Subtype(..) => None, - ty::PredicateAtom::RegionOutlives(..) => None, - ty::PredicateAtom::TypeOutlives(..) => None, - ty::PredicateAtom::WellFormed(..) => None, - ty::PredicateAtom::ObjectSafe(..) => None, - ty::PredicateAtom::ConstEvaluatable(..) => None, - ty::PredicateAtom::ConstEquate(..) => None, + ty::PredicateKind::Subtype(..) => None, + ty::PredicateKind::RegionOutlives(..) => None, + ty::PredicateKind::TypeOutlives(..) => None, + ty::PredicateKind::WellFormed(..) => None, + ty::PredicateKind::ObjectSafe(..) => None, + ty::PredicateKind::ConstEvaluatable(..) => None, + ty::PredicateKind::ConstEquate(..) => None, // N.B., this predicate is created by breaking down a // `ClosureType: FnFoo()` predicate, where // `ClosureType` represents some `Closure`. It can't @@ -787,8 +790,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // this closure yet; this is exactly why the other // code is looking for a self type of a unresolved // inference variable. - ty::PredicateAtom::ClosureKind(..) => None, - ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, + ty::PredicateKind::ClosureKind(..) => None, + ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } }) .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) @@ -904,8 +907,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // Return directly on cache hit. This is useful to avoid doubly reporting // errors with default match binding modes. See #44614. - let def = - cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err); + let def = cached_result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)); return (def, Some(ty), slice::from_ref(&**item_segment)); } let item_name = item_segment.ident; @@ -914,7 +916,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), _ => Err(ErrorReported), }; - if item_name.name != kw::Invalid { + if item_name.name != kw::Empty { if let Some(mut e) = self.report_method_error( span, ty, @@ -932,7 +934,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Write back the new resolution. self.write_resolution(hir_id, result); ( - result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), + result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), Some(ty), slice::from_ref(&**item_segment), ) @@ -1164,7 +1166,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container); match container { ty::TraitContainer(trait_did) => { - callee::check_legal_trait_for_method_call(tcx, span, None, trait_did) + callee::check_legal_trait_for_method_call(tcx, span, None, span, trait_did) } ty::ImplContainer(impl_def_id) => { if segments.len() == 1 { @@ -1219,18 +1221,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // a problem. let mut infer_args_for_err = FxHashSet::default(); + for &PathSeg(def_id, index) in &path_segs { let seg = &segments[index]; let generics = tcx.generics_of(def_id); + // Argument-position `impl Trait` is treated as a normal generic // parameter internally, but we don't allow users to specify the // parameter's value explicitly, so we have to do some error- // checking here. if let GenericArgCountResult { - correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }), + correct: Err(GenericArgCountMismatch { reported: Some(_), .. }), .. } = AstConv::check_generic_arg_count_for_call( - tcx, span, &generics, &seg, false, // `is_method_call` + tcx, + span, + def_id, + &generics, + seg, + IsMethodCall::No, ) { infer_args_for_err.insert(index); self.set_tainted_by_errors(); // See issue #53251. @@ -1376,7 +1385,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } GenericParamDefKind::Const => { - // FIXME(const_generics:defaults) + // FIXME(const_generics_defaults) // No const parameters were provided, we have to infer them. self.fcx.var_for_def(self.span, param) } @@ -1473,7 +1482,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } else { if !self.is_tainted_by_errors() { - self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282) + self.emit_inference_failure_err((**self).body_id, sp, ty.into(), vec![], E0282) .note("type must be known at this point") .emit(); } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 333bda00db..3326be796c 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -22,7 +22,7 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::{self, MultiSpan, Span}; use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression}; -use std::mem::replace; +use crate::structured_errors::StructuredDiagnostic; use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -173,18 +173,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(def_id) = def_id { - if let Some(node) = tcx.hir().get_if_local(def_id) { - let mut spans: MultiSpan = node - .ident() - .map(|ident| ident.span) - .unwrap_or_else(|| tcx.hir().span(node.hir_id().unwrap())) - .into(); - - if let Some(id) = node.body_id() { - let body = tcx.hir().body(id); - for param in body.params { - spans.push_span_label(param.span, String::new()); - } + if let Some(def_span) = tcx.def_ident_span(def_id) { + let mut spans: MultiSpan = def_span.into(); + + let params = tcx + .hir() + .get_if_local(def_id) + .and_then(|node| node.body_id()) + .into_iter() + .map(|id| tcx.hir().body(id).params) + .flatten(); + + for param in params { + spans.push_span_label(param.span, String::new()); } let def_kind = tcx.def_kind(def_id); @@ -325,10 +326,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); } - let is_closure = match arg.kind { - ExprKind::Closure(..) => true, - _ => false, - }; + let is_closure = matches!(arg.kind, ExprKind::Closure(..)); if is_closure != check_closures { continue; @@ -361,9 +359,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We also need to make sure we at least write the ty of the other // arguments which we skipped above. if c_variadic { - fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { - use crate::structured_errors::{StructuredDiagnostic, VariadicError}; - VariadicError::new(s, span, t, cast_ty).diagnostic().emit(); + fn variadic_error<'tcx>(sess: &Session, span: Span, ty: Ty<'tcx>, cast_ty: &str) { + use crate::structured_errors::MissingCastForVariadicArg; + + MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit() } for arg in args.iter().skip(expected_arg_count) { @@ -373,13 +372,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // in C but we just error out instead and require explicit casts. let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); match arg_ty.kind() { - ty::Float(ast::FloatTy::F32) => { + ty::Float(ty::FloatTy::F32) => { variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); } - ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => { + ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => { variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); } - ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => { + ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => { variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); } ty::FnDef(..) => { @@ -408,8 +407,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ast::LitKind::Byte(_) => tcx.types.u8, ast::LitKind::Char(_) => tcx.types.char, - ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t), - ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t), + ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(ty::int_ty(t)), + ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(ty::uint_ty(t)), ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { ty::Int(_) | ty::Uint(_) => Some(ty), @@ -420,7 +419,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); opt_ty.unwrap_or_else(|| self.next_int_var()) } - ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t), + ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => { + tcx.mk_mach_float(ty::float_ty(t)) + } ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { ty::Float(_) => Some(ty), @@ -589,11 +590,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { blk: &'tcx hir::Block<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let prev = { - let mut fcx_ps = self.ps.borrow_mut(); - let unsafety_state = fcx_ps.recurse(blk); - replace(&mut *fcx_ps, unsafety_state) - }; + let prev = self.ps.replace(self.ps.get().recurse(blk)); // In some cases, blocks have just one exit, but other blocks // can be targeted by multiple breaks. This can happen both @@ -709,7 +706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_ty(blk.hir_id, ty); - *self.ps.borrow_mut() = prev; + self.ps.set(prev); ty } @@ -806,33 +803,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// // ^^^^ point at this instead of the whole `if` expression /// ``` fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { - if let hir::ExprKind::Match(_, arms, _) = &expr.kind { - let arm_spans: Vec = arms - .iter() - .filter_map(|arm| { - self.in_progress_typeck_results - .and_then(|typeck_results| { - typeck_results.borrow().node_type_opt(arm.body.hir_id) - }) - .and_then(|arm_ty| { - if arm_ty.is_never() { - None - } else { - Some(match &arm.body.kind { - // Point at the tail expression when possible. - hir::ExprKind::Block(block, _) => { - block.expr.as_ref().map(|e| e.span).unwrap_or(block.span) - } - _ => arm.body.span, - }) + let check_in_progress = |elem: &hir::Expr<'_>| { + self.in_progress_typeck_results + .and_then(|typeck_results| typeck_results.borrow().node_type_opt(elem.hir_id)) + .and_then(|ty| { + if ty.is_never() { + None + } else { + Some(match elem.kind { + // Point at the tail expression when possible. + hir::ExprKind::Block(block, _) => { + block.expr.map_or(block.span, |e| e.span) } + _ => elem.span, }) + } }) - .collect(); - if arm_spans.len() == 1 { - return arm_spans[0]; + }; + + if let hir::ExprKind::If(_, _, Some(el)) = expr.kind { + if let Some(rslt) = check_in_progress(el) { + return rslt; } } + + if let hir::ExprKind::Match(_, arms, _) = expr.kind { + let mut iter = arms.iter().filter_map(|arm| check_in_progress(arm.body)); + if let Some(span) = iter.next() { + if iter.next().is_none() { + return span; + } + } + } + expr.span } @@ -882,7 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Write back the new resolution. self.write_resolution(hir_id, result); - (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty) + (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) } QPath::LangItem(lang_item, span) => { self.resolve_lang_item_path(lang_item, span, hir_id) @@ -917,8 +920,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } - if let ty::PredicateAtom::Trait(predicate, _) = - error.obligation.predicate.skip_binders() + if let ty::PredicateKind::Trait(predicate, _) = + error.obligation.predicate.kind().skip_binder() { // Collect the argument position for all arguments that could have caused this // `FulfillmentError`. @@ -968,8 +971,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::ExprKind::Path(qpath) = &path.kind { if let hir::QPath::Resolved(_, path) = &qpath { for error in errors { - if let ty::PredicateAtom::Trait(predicate, _) = - error.obligation.predicate.skip_binders() + if let ty::PredicateKind::Trait(predicate, _) = + error.obligation.predicate.kind().skip_binder() { // If any of the type arguments in this path segment caused the // `FullfillmentError`, point at its span (#61860). diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs index f635e0b6f9..e9223f700d 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs @@ -66,11 +66,11 @@ pub struct FnCtxt<'a, 'tcx> { pub(super) in_tail_expr: bool, /// First span of a return site that we find. Used in error messages. - pub(super) ret_coercion_span: RefCell>, + pub(super) ret_coercion_span: Cell>, pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, - pub(super) ps: RefCell, + pub(super) ps: Cell, /// Whether the last checked node generates a divergence (e.g., /// `return` will set this to `Always`). In general, when entering @@ -127,9 +127,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ret_coercion_impl_trait: None, ret_type_span: None, in_tail_expr: false, - ret_coercion_span: RefCell::new(None), + ret_coercion_span: Cell::new(None), resume_yield_tys: None, - ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), + ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), diverges: Cell::new(Diverges::Maybe), has_errors: Cell::new(false), enclosing_breakables: RefCell::new(EnclosingBreakables { @@ -194,8 +194,8 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { parent: None, predicates: tcx.arena.alloc_from_iter( self.param_env.caller_bounds().iter().filter_map(|predicate| { - match predicate.skip_binders() { - ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => { + match predicate.kind().skip_binder() { + ty::PredicateKind::Trait(data, _) if data.self_ty().is_param(index) => { // HACK(eddyb) should get the original `Span`. let span = tcx.def_span(def_id); Some((predicate, span)) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 17dbf989d6..a0465ca6ae 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -2,7 +2,7 @@ use super::FnCtxt; use crate::astconv::AstConv; use rustc_ast::util::parser::ExprPrecedence; -use rustc_span::{self, Span}; +use rustc_span::{self, MultiSpan, Span}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; @@ -287,6 +287,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// When encountering a closure that captures variables, where a FnPtr is expected, + /// suggest a non-capturing closure + pub(in super::super) fn suggest_no_capture_closure( + &self, + err: &mut DiagnosticBuilder<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { + if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { + // Report upto four upvars being captured to reduce the amount error messages + // reported back to the user. + let spans_and_labels = upvars + .iter() + .take(4) + .map(|(var_hir_id, upvar)| { + let var_name = self.tcx.hir().name(*var_hir_id).to_string(); + let msg = format!("`{}` captured here", var_name); + (upvar.span, msg) + }) + .collect::>(); + + let mut multi_span: MultiSpan = + spans_and_labels.iter().map(|(sp, _)| *sp).collect::>().into(); + for (sp, label) in spans_and_labels { + multi_span.push_span_label(sp, label); + } + err.span_note(multi_span, "closures can only be coerced to `fn` types if they do not capture any variables"); + } + } + } + /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. pub(in super::super) fn suggest_calling_boxed_future_when_appropriate( &self, @@ -358,6 +390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Loop(..) + | ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) => { err.span_suggestion( diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index c3b0fc60b9..e99db7a247 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -63,8 +63,6 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::min_align_of | sym::needs_drop | sym::caller_location - | sym::size_of_val - | sym::min_align_of_val | sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow @@ -92,6 +90,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::rustc_peek | sym::maxnumf64 | sym::type_name + | sym::forget | sym::variant_count => hir::Unsafety::Normal, _ => hir::Unsafety::Unsafe, } @@ -158,7 +157,6 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { } sym::forget => (1, vec![param(0)], tcx.mk_unit()), sym::transmute => (2, vec![param(0)], param(1)), - sym::move_val_init => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), sym::prefetch_read_data | sym::prefetch_write_data | sym::prefetch_read_instruction diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index 8ef723d590..e5f19281b0 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -1,6 +1,6 @@ use super::{probe, MethodCallee}; -use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt}; +use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; use crate::check::{callee, FnCtxt}; use crate::hir::def_id::DefId; use crate::hir::GenericArg; @@ -31,6 +31,7 @@ impl<'a, 'tcx> Deref for ConfirmContext<'a, 'tcx> { } } +#[derive(Debug)] pub struct ConfirmResult<'tcx> { pub callee: MethodCallee<'tcx>, pub illegal_sized_bound: Option, @@ -298,8 +299,14 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // If they were not explicitly supplied, just construct fresh // variables. let generics = self.tcx.generics_of(pick.item.def_id); + let arg_count_correct = AstConv::check_generic_arg_count_for_call( - self.tcx, self.span, &generics, &seg, true, // `is_method_call` + self.tcx, + self.span, + pick.item.def_id, + &generics, + seg, + IsMethodCall::Yes, ); // Create subst for early-bound lifetime parameters, combining @@ -472,8 +479,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied()) // We don't care about regions here. - .filter_map(|obligation| match obligation.predicate.skip_binders() { - ty::PredicateAtom::Trait(trait_pred, _) if trait_pred.def_id() == sized_def_id => { + .filter_map(|obligation| match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(trait_pred, _) if trait_pred.def_id() == sized_def_id => { let span = predicates .predicates .iter() @@ -501,6 +508,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.tcx, self.span, Some(self.self_expr.span), + self.call_expr.span, trait_def_id, ), ty::ImplContainer(..) => {} diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 8e13b37469..9a3d1e42b7 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -102,6 +102,7 @@ pub enum CandidateSource { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Determines whether the type `self_ty` supports a method name `method_name` or not. + #[instrument(level = "debug", skip(self))] pub fn method_exists( &self, method_name: Ident, @@ -129,6 +130,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Adds a suggestion to call the given method to the provided diagnostic. + #[instrument(level = "debug", skip(self, err, call_expr))] crate fn suggest_method_call( &self, err: &mut DiagnosticBuilder<'a>, @@ -177,6 +179,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// * `span`: the span for the method call /// * `call_expr`: the complete method call: (`foo.bar::(...)`) /// * `self_expr`: the self expression (`foo`) + #[instrument(level = "debug", skip(self, call_expr, self_expr))] pub fn lookup_method( &self, self_ty: Ty<'tcx>, @@ -204,6 +207,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment); + debug!("result = {:?}", result); if let Some(span) = result.illegal_sized_bound { let mut needs_mut = false; @@ -256,6 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(result.callee) } + #[instrument(level = "debug", skip(self, call_expr))] pub fn lookup_probe( &self, span: Span, @@ -286,6 +291,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME(#18741): it seems likely that we can consolidate some of this // code with the other method-lookup code. In particular, the second half // of this method is basically the same as confirmation. + #[instrument(level = "debug", skip(self, span, opt_input_types))] pub fn lookup_method_in_trait( &self, span: Span, @@ -399,7 +405,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { obligations.push(traits::Obligation::new( cause, self.param_env, - ty::PredicateAtom::WellFormed(method_ty.into()).to_predicate(tcx), + ty::PredicateKind::WellFormed(method_ty.into()).to_predicate(tcx), )); let callee = MethodCallee { def_id, substs: trait_ref.substs, sig: fn_sig }; @@ -409,6 +415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(InferOk { obligations, value: callee }) } + #[instrument(level = "debug", skip(self))] pub fn resolve_ufcs( &self, span: Span, diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 891dd8b2f0..158c214759 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -8,7 +8,6 @@ use crate::errors::MethodCallOnUnknownType; use crate::hir::def::DefKind; use crate::hir::def_id::DefId; -use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; @@ -48,7 +47,7 @@ pub use self::PickKind::*; /// Boolean flag used to indicate if this search is for a suggestion /// or not. If true, we can allow ambiguity and so forth. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct IsSuggestion(pub bool); struct ProbeContext<'a, 'tcx> { @@ -219,6 +218,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// would result in an error (basically, the same criteria we /// would use to decide if a method is a plausible fit for /// ambiguity purposes). + #[instrument(level = "debug", skip(self, scope_expr_id))] pub fn probe_for_return_type( &self, span: Span, @@ -264,6 +264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } + #[instrument(level = "debug", skip(self, scope_expr_id))] pub fn probe_for_name( &self, span: Span, @@ -423,9 +424,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { probe_cx.assemble_inherent_candidates(); match scope { ProbeScope::TraitsInScope => { - probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)? + probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id) } - ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits()?, + ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(), }; op(probe_cx) }) @@ -660,30 +661,30 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } ty::Int(i) => { let lang_def_id = match i { - ast::IntTy::I8 => lang_items.i8_impl(), - ast::IntTy::I16 => lang_items.i16_impl(), - ast::IntTy::I32 => lang_items.i32_impl(), - ast::IntTy::I64 => lang_items.i64_impl(), - ast::IntTy::I128 => lang_items.i128_impl(), - ast::IntTy::Isize => lang_items.isize_impl(), + ty::IntTy::I8 => lang_items.i8_impl(), + ty::IntTy::I16 => lang_items.i16_impl(), + ty::IntTy::I32 => lang_items.i32_impl(), + ty::IntTy::I64 => lang_items.i64_impl(), + ty::IntTy::I128 => lang_items.i128_impl(), + ty::IntTy::Isize => lang_items.isize_impl(), }; self.assemble_inherent_impl_for_primitive(lang_def_id); } ty::Uint(i) => { let lang_def_id = match i { - ast::UintTy::U8 => lang_items.u8_impl(), - ast::UintTy::U16 => lang_items.u16_impl(), - ast::UintTy::U32 => lang_items.u32_impl(), - ast::UintTy::U64 => lang_items.u64_impl(), - ast::UintTy::U128 => lang_items.u128_impl(), - ast::UintTy::Usize => lang_items.usize_impl(), + ty::UintTy::U8 => lang_items.u8_impl(), + ty::UintTy::U16 => lang_items.u16_impl(), + ty::UintTy::U32 => lang_items.u32_impl(), + ty::UintTy::U64 => lang_items.u64_impl(), + ty::UintTy::U128 => lang_items.u128_impl(), + ty::UintTy::Usize => lang_items.usize_impl(), }; self.assemble_inherent_impl_for_primitive(lang_def_id); } ty::Float(f) => { let (lang_def_id1, lang_def_id2) = match f { - ast::FloatTy::F32 => (lang_items.f32_impl(), lang_items.f32_runtime_impl()), - ast::FloatTy::F64 => (lang_items.f64_impl(), lang_items.f64_runtime_impl()), + ty::FloatTy::F32 => (lang_items.f32_impl(), lang_items.f32_runtime_impl()), + ty::FloatTy::F64 => (lang_items.f64_impl(), lang_items.f64_runtime_impl()), }; self.assemble_inherent_impl_for_primitive(lang_def_id1); self.assemble_inherent_impl_for_primitive(lang_def_id2); @@ -770,7 +771,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // will be reported by `object_safety.rs` if the method refers to the // `Self` type anywhere other than the receiver. Here, we use a // substitution that replaces `Self` with the object type itself. Hence, - // a `&self` method will wind up with an argument type like `&Trait`. + // a `&self` method will wind up with an argument type like `&dyn Trait`. let trait_ref = principal.with_self_ty(self.tcx, self_ty); self.elaborate_bounds(iter::once(trait_ref), |this, new_trait_ref, item| { let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); @@ -795,9 +796,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty); let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { - let bound_predicate = predicate.bound_atom(); + let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(trait_predicate, _) => { + ty::PredicateKind::Trait(trait_predicate, _) => { match *trait_predicate.trait_ref.self_ty().kind() { ty::Param(p) if p == param_ty => { Some(bound_predicate.rebind(trait_predicate.trait_ref)) @@ -805,16 +806,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { _ => None, } } - ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::Projection(..) - | ty::PredicateAtom::RegionOutlives(..) - | ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::TypeOutlives(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, + ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } }); @@ -866,35 +867,29 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - fn assemble_extension_candidates_for_traits_in_scope( - &mut self, - expr_hir_id: hir::HirId, - ) -> Result<(), MethodError<'tcx>> { + fn assemble_extension_candidates_for_traits_in_scope(&mut self, expr_hir_id: hir::HirId) { let mut duplicates = FxHashSet::default(); let opt_applicable_traits = self.tcx.in_scope_traits(expr_hir_id); if let Some(applicable_traits) = opt_applicable_traits { for trait_candidate in applicable_traits.iter() { let trait_did = trait_candidate.def_id; if duplicates.insert(trait_did) { - let result = self.assemble_extension_candidates_for_trait( + self.assemble_extension_candidates_for_trait( &trait_candidate.import_ids, trait_did, ); - result?; } } } - Ok(()) } - fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(), MethodError<'tcx>> { + fn assemble_extension_candidates_for_all_traits(&mut self) { let mut duplicates = FxHashSet::default(); for trait_info in suggest::all_traits(self.tcx) { if duplicates.insert(trait_info.def_id) { - self.assemble_extension_candidates_for_trait(&smallvec![], trait_info.def_id)?; + self.assemble_extension_candidates_for_trait(&smallvec![], trait_info.def_id); } } - Ok(()) } pub fn matches_return_type( @@ -932,7 +927,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &mut self, import_ids: &SmallVec<[LocalDefId; 1]>, trait_def_id: DefId, - ) -> Result<(), MethodError<'tcx>> { + ) { debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); let trait_substs = self.fresh_item_substs(trait_def_id); let trait_ref = ty::TraitRef::new(trait_def_id, trait_substs); @@ -980,7 +975,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ); } } - Ok(()) } fn candidate_method_names(&self) -> Vec { @@ -1027,7 +1021,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let span = self.span; let tcx = self.tcx; - self.assemble_extension_candidates_for_all_traits()?; + self.assemble_extension_candidates_for_all_traits(); let out_of_scope_traits = match self.pick_core() { Some(Ok(p)) => vec![p.item.container.id()], diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 3bf41981ef..d49c7cae82 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -333,7 +333,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::Path(ref qpath) => { // local binding - if let &QPath::Resolved(_, ref path) = &qpath { + if let QPath::Resolved(_, path) = qpath { if let hir::def::Res::Local(hir_id) = path.res { let span = tcx.hir().span(hir_id); let snippet = tcx.sess.source_map().span_to_snippet(span); @@ -446,6 +446,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + let mut label_span_not_found = || { + if unsatisfied_predicates.is_empty() { + err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); + } else { + err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds")); + } + self.tcx.sess.trait_methods_not_found.borrow_mut().insert(orig_span); + }; + // If the method name is the name of a field with a function or closure type, // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { @@ -501,12 +510,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let field_kind = if is_accessible { "field" } else { "private field" }; err.span_label(item_name.span, format!("{}, not a method", field_kind)); } else if lev_candidate.is_none() && static_sources.is_empty() { - err.span_label(span, format!("{} not found in `{}`", item_kind, ty_str)); - self.tcx.sess.trait_methods_not_found.borrow_mut().insert(orig_span); + label_span_not_found(); } } else { - err.span_label(span, format!("{} not found in `{}`", item_kind, ty_str)); - self.tcx.sess.trait_methods_not_found.borrow_mut().insert(orig_span); + label_span_not_found(); } if self.is_fn_ty(&rcvr_ty, span) { @@ -582,8 +589,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut collect_type_param_suggestions = |self_ty: Ty<'tcx>, parent_pred: &ty::Predicate<'tcx>, obligation: &str| { // We don't care about regions here, so it's fine to skip the binder here. - if let (ty::Param(_), ty::PredicateAtom::Trait(p, _)) = - (self_ty.kind(), parent_pred.skip_binders()) + if let (ty::Param(_), ty::PredicateKind::Trait(p, _)) = + (self_ty.kind(), parent_pred.kind().skip_binder()) { if let ty::Adt(def, _) = p.trait_ref.self_ty().kind() { let node = def.did.as_local().map(|def_id| { @@ -637,9 +644,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; let mut format_pred = |pred: ty::Predicate<'tcx>| { - let bound_predicate = pred.bound_atom(); + let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { - ty::PredicateAtom::Projection(pred) => { + ty::PredicateKind::Projection(pred) => { let pred = bound_predicate.rebind(pred); // `::Item = String`. let trait_ref = @@ -658,7 +665,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_span_label(trait_ref.self_ty(), &obligation, &quiet); Some((obligation, trait_ref.self_ty())) } - ty::PredicateAtom::Trait(poly_trait_ref, _) => { + ty::PredicateKind::Trait(poly_trait_ref, _) => { let p = poly_trait_ref.trait_ref; let self_ty = p.self_ty(); let path = p.print_only_trait_path(); @@ -721,10 +728,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|(_, path)| path) .collect::>() .join("\n"); + let actual_prefix = actual.prefix_string(); + err.set_primary_message(&format!( + "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied" + )); err.note(&format!( - "the method `{}` exists but the following trait bounds were not \ - satisfied:\n{}", - item_name, bound_list + "the following trait bounds were not satisfied:\n{bound_list}" )); } } @@ -742,7 +751,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - if actual.is_enum() { + // Don't emit a suggestion if we found an actual method + // that had unsatisfied trait bounds + if unsatisfied_predicates.is_empty() && actual.is_enum() { let adt_def = actual.ty_adt_def().expect("enum is not an ADT"); if let Some(suggestion) = lev_distance::find_best_match_for_name( &adt_def.variants.iter().map(|s| s.ident.name).collect::>(), @@ -778,17 +789,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(span, msg); } } else if let Some(lev_candidate) = lev_candidate { - let def_kind = lev_candidate.kind.as_def_kind(); - err.span_suggestion( - span, - &format!( - "there is {} {} with a similar name", - def_kind.article(), - def_kind.descr(lev_candidate.def_id), - ), - lev_candidate.ident.to_string(), - Applicability::MaybeIncorrect, - ); + // Don't emit a suggestion if we found an actual method + // that had unsatisfied trait bounds + if unsatisfied_predicates.is_empty() { + let def_kind = lev_candidate.kind.as_def_kind(); + err.span_suggestion( + span, + &format!( + "there is {} {} with a similar name", + def_kind.article(), + def_kind.descr(lev_candidate.def_id), + ), + lev_candidate.ident.to_string(), + Applicability::MaybeIncorrect, + ); + } } return Some(err); @@ -992,11 +1007,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // implementing a trait would be legal but is rejected // here). unsatisfied_predicates.iter().all(|(p, _)| { - match p.skip_binders() { + match p.kind().skip_binder() { // Hide traits if they are present in predicates as they can be fixed without // having to implement them. - ty::PredicateAtom::Trait(t, _) => t.def_id() == info.def_id, - ty::PredicateAtom::Projection(p) => { + ty::PredicateKind::Trait(t, _) => t.def_id() == info.def_id, + ty::PredicateKind::Projection(p) => { p.projection_ty.item_def_id == info.def_id } _ => false, @@ -1193,7 +1208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .any(|imp_did| { let imp = self.tcx.impl_trait_ref(imp_did).unwrap(); let imp_simp = simplify_type(self.tcx, imp.self_ty(), true); - imp_simp.map(|s| s == simp_rcvr_ty).unwrap_or(false) + imp_simp.map_or(false, |s| s == simp_rcvr_ty) }) { explicitly_negative.push(candidate); @@ -1270,11 +1285,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match ty.kind() { ty::Adt(def, _) => def.did.is_local(), ty::Foreign(did) => did.is_local(), - - ty::Dynamic(ref tr, ..) => { - tr.principal().map(|d| d.def_id().is_local()).unwrap_or(false) - } - + ty::Dynamic(ref tr, ..) => tr.principal().map_or(false, |d| d.def_id().is_local()), ty::Param(_) => true, // Everything else (primitive types, etc.) is effectively diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 8177b363a5..dc3e3b4e73 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -52,13 +52,13 @@ The types of top-level items, which never contain unbound type variables, are stored directly into the `tcx` typeck_results. N.B., a type variable is not the same thing as a type parameter. A -type variable is rather an "instance" of a type parameter: that is, -given a generic function `fn foo(t: T)`: while checking the +type variable is an instance of a type parameter. That is, +given a generic function `fn foo(t: T)`, while checking the function `foo`, the type `ty_param(0)` refers to the type `T`, which -is treated in abstract. When `foo()` is called, however, `T` will be +is treated in abstract. However, when `foo()` is called, `T` will be substituted for a fresh type variable `N`. This variable will eventually be resolved to some concrete type (which might itself be -type parameter). +a type parameter). */ @@ -184,14 +184,14 @@ impl UnsafetyState { UnsafetyState { def, unsafety, unsafe_push_count: 0, from_fn: true } } - pub fn recurse(&mut self, blk: &hir::Block<'_>) -> UnsafetyState { + pub fn recurse(self, blk: &hir::Block<'_>) -> UnsafetyState { use hir::BlockCheckMode; match self.unsafety { // If this unsafe, then if the outer function was already marked as // unsafe we shouldn't attribute the unsafe'ness to the block. This // way the block can be warned about instead of ignoring this // extraneous block (functions are never warned about). - hir::Unsafety::Unsafe if self.from_fn => *self, + hir::Unsafety::Unsafe if self.from_fn => self, unsafety => { let (unsafety, def, count) = match blk.rules { @@ -864,9 +864,9 @@ fn bounds_from_generic_predicates<'tcx>( let mut projections = vec![]; for (predicate, _) in predicates.predicates { debug!("predicate {:?}", predicate); - let bound_predicate = predicate.bound_atom(); + let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(trait_predicate, _) => { + ty::PredicateKind::Trait(trait_predicate, _) => { let entry = types.entry(trait_predicate.self_ty()).or_default(); let def_id = trait_predicate.def_id(); if Some(def_id) != tcx.lang_items().sized_trait() { @@ -875,7 +875,7 @@ fn bounds_from_generic_predicates<'tcx>( entry.push(trait_predicate.def_id()); } } - ty::PredicateAtom::Projection(projection_pred) => { + ty::PredicateKind::Projection(projection_pred) => { projections.push(bound_predicate.rebind(projection_pred)); } _ => {} diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 5fc573a57a..d7e69668e5 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -15,6 +15,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::Ident; +use rustc_span::{BytePos, DUMMY_SP}; use rustc_trait_selection::traits::{ObligationCause, Pattern}; use std::cmp; @@ -1001,7 +1002,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // More generally, the expected type wants a tuple variant with one field of an // N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern // with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`. - let missing_parenthesis = match (&expected.kind(), fields, had_err) { + let missing_parentheses = match (&expected.kind(), fields, had_err) { // #67037: only do this if we could successfully type-check the expected type against // the tuple struct pattern. Otherwise the substs could get out of range on e.g., // `let P() = U;` where `P != U` with `struct P(T);`. @@ -1014,13 +1015,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => false, }; - if missing_parenthesis { + if missing_parentheses { let (left, right) = match subpats { // This is the zero case; we aim to get the "hi" part of the `QPath`'s // span as the "lo" and then the "hi" part of the pattern's span as the "hi". // This looks like: // - // help: missing parenthesis + // help: missing parentheses // | // L | let A(()) = A(()); // | ^ ^ @@ -1029,17 +1030,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // last sub-pattern. In the case of `A(x)` the first and last may coincide. // This looks like: // - // help: missing parenthesis + // help: missing parentheses // | // L | let A((x, y)) = A((1, 2)); // | ^ ^ [first, ..] => (first.span.shrink_to_lo(), subpats.last().unwrap().span), }; err.multipart_suggestion( - "missing parenthesis", + "missing parentheses", vec![(left, "(".to_string()), (right.shrink_to_hi(), ")".to_string())], Applicability::MachineApplicable, ); + } else if fields.len() > subpats.len() && pat_span != DUMMY_SP { + let after_fields_span = pat_span.with_hi(pat_span.hi() - BytePos(1)).shrink_to_hi(); + let all_fields_span = match subpats { + [] => after_fields_span, + [field] => field.span, + [first, .., last] => first.span.to(last.span), + }; + + // Check if all the fields in the pattern are wildcards. + let all_wildcards = subpats.iter().all(|pat| matches!(pat.kind, PatKind::Wild)); + let first_tail_wildcard = + subpats.iter().enumerate().fold(None, |acc, (pos, pat)| match (acc, &pat.kind) { + (None, PatKind::Wild) => Some(pos), + (Some(_), PatKind::Wild) => acc, + _ => None, + }); + let tail_span = match first_tail_wildcard { + None => after_fields_span, + Some(0) => subpats[0].span.to(after_fields_span), + Some(pos) => subpats[pos - 1].span.shrink_to_hi().to(after_fields_span), + }; + + // FIXME: heuristic-based suggestion to check current types for where to add `_`. + let mut wildcard_sugg = vec!["_"; fields.len() - subpats.len()].join(", "); + if !subpats.is_empty() { + wildcard_sugg = String::from(", ") + &wildcard_sugg; + } + + err.span_suggestion_verbose( + after_fields_span, + "use `_` to explicitly ignore each field", + wildcard_sugg, + Applicability::MaybeIncorrect, + ); + + // Only suggest `..` if more than one field is missing + // or the pattern consists of all wildcards. + if fields.len() - subpats.len() > 1 || all_wildcards { + if subpats.is_empty() || all_wildcards { + err.span_suggestion_verbose( + all_fields_span, + "use `..` to ignore all fields", + String::from(".."), + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion_verbose( + tail_span, + "use `..` to ignore the rest of the fields", + String::from(", .."), + Applicability::MaybeIncorrect, + ); + } + } } err.emit(); @@ -1439,11 +1494,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns a diagnostic reporting a struct pattern which does not mention some fields. /// /// ```text - /// error[E0027]: pattern does not mention field `you_cant_use_this_field` + /// error[E0027]: pattern does not mention field `bar` /// --> src/main.rs:15:9 /// | /// LL | let foo::Foo {} = foo::Foo::new(); - /// | ^^^^^^^^^^^ missing field `you_cant_use_this_field` + /// | ^^^^^^^^^^^ missing field `bar` /// ``` fn error_unmentioned_fields( &self, @@ -1477,14 +1532,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => return err, }, - [.., field] => ( - match pat.kind { - PatKind::Struct(_, [_, ..], _) => ", ", - _ => "", - }, - "", - field.span.shrink_to_hi(), - ), + [.., field] => { + // Account for last field having a trailing comma or parse recovery at the tail of + // the pattern to avoid invalid suggestion (#78511). + let tail = field.span.shrink_to_hi().with_hi(pat.span.hi()); + match &pat.kind { + PatKind::Struct(..) => (", ", " }", tail), + _ => return err, + } + } }; err.span_suggestion( sp, diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index b8b98cef76..88e8dd3cb1 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -325,7 +325,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { pat.each_binding(|_, hir_id, span, _| { let typ = self.resolve_node_type(hir_id); let body_id = self.body_id; - let _ = dropck::check_drop_obligations(self, typ, span, body_id); + dropck::check_drop_obligations(self, typ, span, body_id); }) } } @@ -354,10 +354,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { hir_id: hir::HirId, ) { assert!( - match fk { - intravisit::FnKind::Closure(..) => true, - _ => false, - }, + matches!(fk, intravisit::FnKind::Closure(..)), "visit_fn invoked for something other than a closure" ); @@ -491,7 +488,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { if place_with_id.place.projections.is_empty() { let typ = self.resolve_type(place_with_id.place.ty()); let body_id = self.body_id; - let _ = dropck::check_drop_obligations(self, typ, span, body_id); + dropck::check_drop_obligations(self, typ, span, body_id); } } } diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 0f084c5c11..04a9e65e66 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -30,6 +30,7 @@ //! then mean that all later passes would have to check for these figments //! and report an error, and it just seems like more mess in the end.) +use super::writeback::Resolver; use super::FnCtxt; use crate::expr_use_visitor as euv; @@ -40,9 +41,11 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults, UpvarSubsts}; +use rustc_session::lint; use rustc_span::sym; -use rustc_span::{Span, Symbol}; +use rustc_span::{MultiSpan, Span, Symbol}; /// Describe the relationship between the paths of two places /// eg: @@ -55,6 +58,11 @@ enum PlaceAncestryRelation { Divergent, } +/// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo` +/// during capture analysis. Information in this map feeds into the minimum capture +/// analysis pass. +type InferredCaptureInformation<'tcx> = FxIndexMap, ty::CaptureInfo<'tcx>>; + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { InferBorrowKindVisitor { fcx: self }.visit_body(body); @@ -92,7 +100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, closure_hir_id: hir::HirId, span: Span, - body: &hir::Body<'_>, + body: &'tcx hir::Body<'tcx>, capture_clause: hir::CaptureBy, ) { debug!("analyze_closure(id={:?}, body.id={:?})", closure_hir_id, body.id()); @@ -124,24 +132,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); - let mut capture_information: FxIndexMap, ty::CaptureInfo<'tcx>> = - Default::default(); - if !self.tcx.features().capture_disjoint_fields { - if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { - for (&var_hir_id, _) in upvars.iter() { - let place = self.place_for_root_variable(local_def_id, var_hir_id); - - debug!("seed place {:?}", place); - - let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id); - let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span); - let info = ty::CaptureInfo { expr_id: None, capture_kind }; - - capture_information.insert(place, info); - } - } - } - let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id()); assert_eq!(body_owner_def_id.to_def_id(), closure_def_id); let mut delegate = InferBorrowKind { @@ -151,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_clause, current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM, current_origin: None, - capture_information, + capture_information: Default::default(), }; euv::ExprUseVisitor::new( &mut delegate, @@ -168,6 +158,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); + self.compute_min_captures(closure_def_id, delegate.capture_information); + + let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); + if should_do_migration_analysis(self.tcx, closure_hir_id) { + self.perform_2229_migration_anaysis(closure_def_id, capture_clause, span, body); + } + + // We now fake capture information for all variables that are mentioned within the closure + // We do this after handling migrations so that min_captures computes before + if !self.tcx.features().capture_disjoint_fields { + let mut capture_information: InferredCaptureInformation<'tcx> = Default::default(); + + if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { + for var_hir_id in upvars.keys() { + let place = self.place_for_root_variable(local_def_id, *var_hir_id); + + debug!("seed place {:?}", place); + + let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id); + let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span); + let fake_info = ty::CaptureInfo { + capture_kind_expr_id: None, + path_expr_id: None, + capture_kind, + }; + + capture_information.insert(place, fake_info); + } + } + + // This will update the min captures based on this new fake information. + self.compute_min_captures(closure_def_id, capture_information); + } + if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure // substs with the kind we inferred. @@ -176,7 +200,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty); // If we have an origin, store it. - if let Some(origin) = delegate.current_origin { + if let Some(origin) = delegate.current_origin.clone() { + let origin = if self.tcx.features().capture_disjoint_fields { + origin + } else { + // FIXME(project-rfc-2229#31): Once the changes to support reborrowing are + // made, make sure we are selecting and restricting + // the origin correctly. + (origin.0, Place { projections: vec![], ..origin.1 }) + }; + self.typeck_results .borrow_mut() .closure_kind_origins_mut() @@ -184,7 +217,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.compute_min_captures(closure_def_id, delegate); self.log_closure_min_capture_info(closure_def_id, span); self.min_captures_to_closure_captures_bridge(closure_def_id); @@ -238,8 +270,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let capture = captured_place.info.capture_kind; debug!( - "place={:?} upvar_ty={:?} capture={:?}", - captured_place.place, upvar_ty, capture + "final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}", + captured_place.place, upvar_ty, capture, captured_place.mutability, ); match capture { @@ -298,8 +330,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(capture_kind) = upvar_capture_map.get(&upvar_id) { // upvar_capture_map only stores the UpvarCapture (CaptureKind), // so we create a fake capture info with no expression. - let fake_capture_info = - ty::CaptureInfo { expr_id: None, capture_kind: *capture_kind }; + let fake_capture_info = ty::CaptureInfo { + capture_kind_expr_id: None, + path_expr_id: None, + capture_kind: *capture_kind, + }; determine_capture_info(fake_capture_info, capture_info).capture_kind } else { capture_info.capture_kind @@ -328,6 +363,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Places (and corresponding capture kind) that we need to keep track of to support all /// the required captured paths. /// + /// + /// Note: If this function is called multiple times for the same closure, it will update + /// the existing min_capture map that is stored in TypeckResults. + /// /// Eg: /// ```rust,no_run /// struct Point { x: i32, y: i32 } @@ -349,48 +388,73 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// ``` /// { - /// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L5, ByValue), - /// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> (hir_id_L2, ByRef(MutBorrow)) - /// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> (hir_id_L3, ByRef(ImmutBorrow)) - /// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L4, ByRef(ImmutBorrow)) + /// Place(base: hir_id_s, projections: [], ....) -> { + /// capture_kind_expr: hir_id_L5, + /// path_expr_id: hir_id_L5, + /// capture_kind: ByValue + /// }, + /// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> { + /// capture_kind_expr: hir_id_L2, + /// path_expr_id: hir_id_L2, + /// capture_kind: ByValue + /// }, + /// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> { + /// capture_kind_expr: hir_id_L3, + /// path_expr_id: hir_id_L3, + /// capture_kind: ByValue + /// }, + /// Place(base: hir_id_p, projections: [], ...) -> { + /// capture_kind_expr: hir_id_L4, + /// path_expr_id: hir_id_L4, + /// capture_kind: ByValue + /// }, /// ``` /// /// After the min capture analysis, we get: /// ``` /// { /// hir_id_s -> [ - /// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L4, ByValue) + /// Place(base: hir_id_s, projections: [], ....) -> { + /// capture_kind_expr: hir_id_L5, + /// path_expr_id: hir_id_L5, + /// capture_kind: ByValue + /// }, /// ], /// hir_id_p -> [ - /// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L2, ByRef(MutBorrow)), + /// Place(base: hir_id_p, projections: [], ...) -> { + /// capture_kind_expr: hir_id_L2, + /// path_expr_id: hir_id_L4, + /// capture_kind: ByValue + /// }, /// ], /// ``` fn compute_min_captures( &self, closure_def_id: DefId, - inferred_info: InferBorrowKind<'_, 'tcx>, + capture_information: InferredCaptureInformation<'tcx>, ) { - let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default(); + if capture_information.is_empty() { + return; + } + + let mut typeck_results = self.typeck_results.borrow_mut(); - for (place, capture_info) in inferred_info.capture_information.into_iter() { + let mut root_var_min_capture_list = + typeck_results.closure_min_captures.remove(&closure_def_id).unwrap_or_default(); + + for (place, capture_info) in capture_information.into_iter() { let var_hir_id = match place.base { PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, base => bug!("Expected upvar, found={:?}", base), }; - // Arrays are captured in entirety, drop Index projections and projections - // after Index projections. - let first_index_projection = - place.projections.split(|proj| ProjectionKind::Index == proj.kind).next(); - let place = Place { - base_ty: place.base_ty, - base: place.base, - projections: first_index_projection.map_or(Vec::new(), |p| p.to_vec()), - }; + let place = restrict_capture_precision(place, capture_info.capture_kind); let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { None => { - let min_cap_list = vec![ty::CapturedPlace { place: place, info: capture_info }]; + let mutability = self.determine_capture_mutability(&typeck_results, &place); + let min_cap_list = + vec![ty::CapturedPlace { place, info: capture_info, mutability }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); continue; } @@ -415,8 +479,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // current place is ancestor of possible_descendant PlaceAncestryRelation::Ancestor => { descendant_found = true; + let backup_path_expr_id = updated_capture_info.path_expr_id; + updated_capture_info = determine_capture_info(updated_capture_info, possible_descendant.info); + + // we need to keep the ancestor's `path_expr_id` + updated_capture_info.path_expr_id = backup_path_expr_id; false } @@ -431,9 +500,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // current place is descendant of possible_ancestor PlaceAncestryRelation::Descendant => { ancestor_found = true; + let backup_path_expr_id = possible_ancestor.info.path_expr_id; possible_ancestor.info = determine_capture_info(possible_ancestor.info, capture_info); + // we need to keep the ancestor's `path_expr_id` + possible_ancestor.info.path_expr_id = backup_path_expr_id; + // Only one ancestor of the current place will be in the list. break; } @@ -444,20 +517,129 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Only need to insert when we don't have an ancestor in the existing min capture list if !ancestor_found { + let mutability = self.determine_capture_mutability(&typeck_results, &place); let captured_place = - ty::CapturedPlace { place: place.clone(), info: updated_capture_info }; + ty::CapturedPlace { place, info: updated_capture_info, mutability }; min_cap_list.push(captured_place); } } debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list); + typeck_results.closure_min_captures.insert(closure_def_id, root_var_min_capture_list); + } - if !root_var_min_capture_list.is_empty() { - self.typeck_results - .borrow_mut() - .closure_min_captures - .insert(closure_def_id, root_var_min_capture_list); + /// Perform the migration analysis for RFC 2229, and emit lint + /// `disjoint_capture_drop_reorder` if needed. + fn perform_2229_migration_anaysis( + &self, + closure_def_id: DefId, + capture_clause: hir::CaptureBy, + span: Span, + body: &'tcx hir::Body<'tcx>, + ) { + let need_migrations = self.compute_2229_migrations_first_pass( + closure_def_id, + span, + capture_clause, + body, + self.typeck_results.borrow().closure_min_captures.get(&closure_def_id), + ); + + if !need_migrations.is_empty() { + let need_migrations_hir_id = need_migrations.iter().map(|m| m.0).collect::>(); + + let migrations_text = migration_suggestion_for_2229(self.tcx, &need_migrations_hir_id); + + let local_def_id = closure_def_id.expect_local(); + let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); + self.tcx.struct_span_lint_hir( + lint::builtin::DISJOINT_CAPTURE_DROP_REORDER, + closure_hir_id, + span, + |lint| { + let mut diagnostics_builder = lint.build( + "drop order affected for closure because of `capture_disjoint_fields`", + ); + diagnostics_builder.note(&migrations_text); + diagnostics_builder.emit(); + }, + ); + } + } + + /// Figures out the list of root variables (and their types) that aren't completely + /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of + /// some path starting at that root variable **might** be affected. + /// + /// The output list would include a root variable if: + /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't + /// enabled, **and** + /// - It wasn't completely captured by the closure, **and** + /// - The type of the root variable needs Drop. + fn compute_2229_migrations_first_pass( + &self, + closure_def_id: DefId, + closure_span: Span, + closure_clause: hir::CaptureBy, + body: &'tcx hir::Body<'tcx>, + min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, + ) -> Vec<(hir::HirId, Ty<'tcx>)> { + fn resolve_ty>( + fcx: &FnCtxt<'_, 'tcx>, + span: Span, + body: &'tcx hir::Body<'tcx>, + ty: T, + ) -> T { + let mut resolver = Resolver::new(fcx, &span, body); + ty.fold_with(&mut resolver) } + + let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { + upvars + } else { + return vec![]; + }; + + let mut need_migrations = Vec::new(); + + for (&var_hir_id, _) in upvars.iter() { + let ty = resolve_ty(self, closure_span, body, self.node_ty(var_hir_id)); + + if !ty.needs_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) { + continue; + } + + let root_var_min_capture_list = if let Some(root_var_min_capture_list) = + min_captures.and_then(|m| m.get(&var_hir_id)) + { + root_var_min_capture_list + } else { + // The upvar is mentioned within the closure but no path starting from it is + // used. + + match closure_clause { + // Only migrate if closure is a move closure + hir::CaptureBy::Value => need_migrations.push((var_hir_id, ty)), + + hir::CaptureBy::Ref => {} + } + + continue; + }; + + let is_moved = root_var_min_capture_list + .iter() + .any(|capture| matches!(capture.info.capture_kind, ty::UpvarCapture::ByValue(_))); + + let is_not_completely_captured = + root_var_min_capture_list.iter().any(|capture| capture.place.projections.len() > 0); + + if is_moved && is_not_completely_captured { + need_migrations.push((var_hir_id, ty)); + } + } + + need_migrations } fn init_capture_kind( @@ -508,7 +690,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let capture_str = construct_capture_info_string(self.tcx, place, capture_info); let output_str = format!("Capturing {}", capture_str); - let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); + let span = + capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); diag.span_note(span, &output_str); } diag.emit(); @@ -532,15 +715,80 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { construct_capture_info_string(self.tcx, place, capture_info); let output_str = format!("Min Capture {}", capture_str); - let span = - capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); - diag.span_note(span, &output_str); + if capture.info.path_expr_id != capture.info.capture_kind_expr_id { + let path_span = capture_info + .path_expr_id + .map_or(closure_span, |e| self.tcx.hir().span(e)); + let capture_kind_span = capture_info + .capture_kind_expr_id + .map_or(closure_span, |e| self.tcx.hir().span(e)); + + let mut multi_span: MultiSpan = + MultiSpan::from_spans(vec![path_span, capture_kind_span]); + + let capture_kind_label = + construct_capture_kind_reason_string(self.tcx, place, capture_info); + let path_label = construct_path_string(self.tcx, place); + + multi_span.push_span_label(path_span, path_label); + multi_span.push_span_label(capture_kind_span, capture_kind_label); + + diag.span_note(multi_span, &output_str); + } else { + let span = capture_info + .path_expr_id + .map_or(closure_span, |e| self.tcx.hir().span(e)); + + diag.span_note(span, &output_str); + }; } } diag.emit(); } } } + + /// A captured place is mutable if + /// 1. Projections don't include a Deref of an immut-borrow, **and** + /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow. + fn determine_capture_mutability( + &self, + typeck_results: &'a TypeckResults<'tcx>, + place: &Place<'tcx>, + ) -> hir::Mutability { + let var_hir_id = match place.base { + PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + _ => unreachable!(), + }; + + let bm = *typeck_results.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); + + let mut is_mutbl = match bm { + ty::BindByValue(mutability) => mutability, + ty::BindByReference(_) => hir::Mutability::Not, + }; + + for pointer_ty in place.deref_tys() { + match pointer_ty.kind() { + // We don't capture derefs of raw ptrs + ty::RawPtr(_) => unreachable!(), + + // Derefencing a mut-ref allows us to mut the Place if we don't deref + // an immut-ref after on top of this. + ty::Ref(.., hir::Mutability::Mut) => is_mutbl = hir::Mutability::Mut, + + // The place isn't mutable once we dereference a immutable reference. + ty::Ref(.., hir::Mutability::Not) => return hir::Mutability::Not, + + // Dereferencing a box doesn't change mutability + ty::Adt(def, ..) if def.is_box() => {} + + unexpected_ty => bug!("deref of unexpected pointer type {:?}", unexpected_ty), + } + } + + is_mutbl + } } struct InferBorrowKind<'a, 'tcx> { @@ -563,7 +811,7 @@ struct InferBorrowKind<'a, 'tcx> { // If we modified `current_closure_kind`, this field contains a `Some()` with the // variable access that caused us to do so. - current_origin: Option<(Span, Symbol)>, + current_origin: Option<(Span, Place<'tcx>)>, /// For each Place that is captured by the closure, we track the minimal kind of /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access. @@ -587,9 +835,11 @@ struct InferBorrowKind<'a, 'tcx> { /// /// For closure `fix_s`, (at a high level) the map contains /// + /// ``` /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } - capture_information: FxIndexMap, ty::CaptureInfo<'tcx>>, + /// ``` + capture_information: InferredCaptureInformation<'tcx>, } impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { @@ -628,11 +878,12 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { upvar_id.closure_expr_id, ty::ClosureKind::FnOnce, usage_span, - var_name(tcx, upvar_id.var_path.hir_id), + place_with_id.place.clone(), ); let capture_info = ty::CaptureInfo { - expr_id: Some(diag_expr_id), + capture_kind_expr_id: Some(diag_expr_id), + path_expr_id: Some(diag_expr_id), capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)), }; @@ -720,7 +971,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { upvar_id.closure_expr_id, ty::ClosureKind::FnMut, tcx.hir().span(diag_expr_id), - var_name(tcx, upvar_id.var_path.hir_id), + place_with_id.place.clone(), ); } } @@ -752,7 +1003,8 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region }; let capture_info = ty::CaptureInfo { - expr_id: Some(diag_expr_id), + capture_kind_expr_id: Some(diag_expr_id), + path_expr_id: Some(diag_expr_id), capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow), }; let updated_info = determine_capture_info(curr_capture_info, capture_info); @@ -765,11 +1017,11 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { closure_id: LocalDefId, new_kind: ty::ClosureKind, upvar_span: Span, - var_name: Symbol, + place: Place<'tcx>, ) { debug!( - "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, var_name={})", - closure_id, new_kind, upvar_span, var_name + "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, place={:?})", + closure_id, new_kind, upvar_span, place ); // Is this the closure whose kind is currently being inferred? @@ -797,7 +1049,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { // new kind is stronger than the old kind self.current_closure_kind = new_kind; - self.current_origin = Some((upvar_span, var_name)); + self.current_origin = Some((upvar_span, place)); } } } @@ -814,7 +1066,11 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span); let expr_id = Some(diag_expr_id); - let capture_info = ty::CaptureInfo { expr_id, capture_kind }; + let capture_info = ty::CaptureInfo { + capture_kind_expr_id: expr_id, + path_expr_id: expr_id, + capture_kind, + }; debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info); @@ -880,11 +1136,67 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { } } -fn construct_capture_info_string( - tcx: TyCtxt<'_>, - place: &Place<'tcx>, - capture_info: &ty::CaptureInfo<'tcx>, -) -> String { +/// Truncate projections so that following rules are obeyed by the captured `place`: +/// +/// - No Derefs in move closure, this will result in value behind a reference getting moved. +/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture +/// them completely. +/// - No Index projections are captured, since arrays are captured completely. +fn restrict_capture_precision<'tcx>( + mut place: Place<'tcx>, + capture_kind: ty::UpvarCapture<'tcx>, +) -> Place<'tcx> { + if place.projections.is_empty() { + // Nothing to do here + return place; + } + + if place.base_ty.is_unsafe_ptr() { + place.projections.truncate(0); + return place; + } + + let mut truncated_length = usize::MAX; + let mut first_deref_projection = usize::MAX; + + for (i, proj) in place.projections.iter().enumerate() { + if proj.ty.is_unsafe_ptr() { + // Don't apply any projections on top of an unsafe ptr + truncated_length = truncated_length.min(i + 1); + break; + } + match proj.kind { + ProjectionKind::Index => { + // Arrays are completely captured, so we drop Index projections + truncated_length = truncated_length.min(i); + break; + } + ProjectionKind::Deref => { + // We only drop Derefs in case of move closures + // There might be an index projection or raw ptr ahead, so we don't stop here. + first_deref_projection = first_deref_projection.min(i); + } + ProjectionKind::Field(..) => {} // ignore + ProjectionKind::Subslice => {} // We never capture this + } + } + + let length = place + .projections + .len() + .min(truncated_length) + // In case of capture `ByValue` we want to not capture derefs + .min(match capture_kind { + ty::UpvarCapture::ByValue(..) => first_deref_projection, + ty::UpvarCapture::ByRef(..) => usize::MAX, + }); + + place.projections.truncate(length); + + place +} + +fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { let variable_name = match place.base { PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(), _ => bug!("Capture_information should only contain upvars"), @@ -904,23 +1216,71 @@ fn construct_capture_info_string( projections_str.push_str(proj.as_str()); } + format!("{}[{}]", variable_name, projections_str) +} + +fn construct_capture_kind_reason_string( + tcx: TyCtxt<'_>, + place: &Place<'tcx>, + capture_info: &ty::CaptureInfo<'tcx>, +) -> String { + let place_str = construct_place_string(tcx, &place); + let capture_kind_str = match capture_info.capture_kind { ty::UpvarCapture::ByValue(_) => "ByValue".into(), ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), }; - format!("{}[{}] -> {}", variable_name, projections_str, capture_kind_str) + + format!("{} captured as {} here", place_str, capture_kind_str) +} + +fn construct_path_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { + let place_str = construct_place_string(tcx, &place); + + format!("{} used here", place_str) +} + +fn construct_capture_info_string( + tcx: TyCtxt<'_>, + place: &Place<'tcx>, + capture_info: &ty::CaptureInfo<'tcx>, +) -> String { + let place_str = construct_place_string(tcx, &place); + + let capture_kind_str = match capture_info.capture_kind { + ty::UpvarCapture::ByValue(_) => "ByValue".into(), + ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), + }; + format!("{} -> {}", place_str, capture_kind_str) } fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } +fn should_do_migration_analysis(tcx: TyCtxt<'_>, closure_id: hir::HirId) -> bool { + let (level, _) = + tcx.lint_level_at_node(lint::builtin::DISJOINT_CAPTURE_DROP_REORDER, closure_id); + + !matches!(level, lint::Level::Allow) +} + +fn migration_suggestion_for_2229(tcx: TyCtxt<'_>, need_migrations: &Vec) -> String { + let need_migrations_strings = + need_migrations.iter().map(|v| format!("{}", var_name(tcx, *v))).collect::>(); + let migrations_list_concat = need_migrations_strings.join(", "); + + format!("drop(&({}));", migrations_list_concat) +} + /// Helper function to determine if we need to escalate CaptureKind from /// CaptureInfo A to B and returns the escalated CaptureInfo. /// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) /// /// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based -/// on the `CaptureInfo` containing an associated expression id. +/// on the `CaptureInfo` containing an associated `capture_kind_expr_id`. +/// +/// It is the caller's duty to figure out which path_expr_id to use. /// /// If both the CaptureKind and Expression are considered to be equivalent, /// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize @@ -971,7 +1331,7 @@ fn determine_capture_info( }; if eq_capture_kind { - match (capture_info_a.expr_id, capture_info_b.expr_id) { + match (capture_info_a.capture_kind_expr_id, capture_info_b.capture_kind_expr_id) { (Some(_), _) | (None, None) => capture_info_a, (None, Some(_)) => capture_info_b, } diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index c09f8cce5b..c90db4786e 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -51,7 +51,7 @@ impl<'tcx> CheckWfFcxBuilder<'tcx> { let fcx = FnCtxt::new(&inh, param_env, id); if !inh.tcx.features().trivial_bounds { // As predicates are cached rather than obligations, this - // needsto be called first so that they are checked with an + // needs to be called first so that they are checked with an // empty `param_env`. check_false_global_bounds(&fcx, span, id); } @@ -103,35 +103,28 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { // // won't be allowed unless there's an *explicit* implementation of `Send` // for `T` - hir::ItemKind::Impl { - defaultness, - defaultness_span, - polarity, - ref of_trait, - ref self_ty, - .. - } => { + hir::ItemKind::Impl(ref impl_) => { let is_auto = tcx .impl_trait_ref(tcx.hir().local_def_id(item.hir_id)) .map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id)); - if let (hir::Defaultness::Default { .. }, true) = (defaultness, is_auto) { - let sp = of_trait.as_ref().map(|t| t.path.span).unwrap_or(item.span); + if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) { + let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span); let mut err = tcx.sess.struct_span_err(sp, "impls of auto traits cannot be default"); - err.span_labels(defaultness_span, "default because of this"); + err.span_labels(impl_.defaultness_span, "default because of this"); err.span_label(sp, "auto trait"); err.emit(); } // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span. - match (tcx.impl_polarity(def_id), polarity) { + match (tcx.impl_polarity(def_id), impl_.polarity) { (ty::ImplPolarity::Positive, _) => { - check_impl(tcx, item, self_ty, of_trait); + check_impl(tcx, item, impl_.self_ty, &impl_.of_trait); } (ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => { // FIXME(#27579): what amount of WF checking do we need for neg impls? - if let hir::Defaultness::Default { .. } = defaultness { + if let hir::Defaultness::Default { .. } = impl_.defaultness { let mut spans = vec![span]; - spans.extend(defaultness_span); + spans.extend(impl_.defaultness_span); struct_span_err!( tcx.sess, spans, @@ -286,14 +279,20 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { // We currently only check wf of const params here. hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => (), - // Const parameters are well formed if their - // type is structural match. - hir::GenericParamKind::Const { ty: hir_ty } => { + // Const parameters are well formed if their type is structural match. + // FIXME(const_generics_defaults): we also need to check that the `default` is wf. + hir::GenericParamKind::Const { ty: hir_ty, default: _ } => { let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id)); let err_ty_str; let mut is_ptr = true; - let err = if tcx.features().min_const_generics { + let err = if tcx.features().const_generics { + match ty.peel_refs().kind() { + ty::FnPtr(_) => Some("function pointers"), + ty::RawPtr(_) => Some("raw pointers"), + _ => None, + } + } else { match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None, ty::FnPtr(_) => Some("function pointers"), @@ -304,12 +303,6 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { Some(err_ty_str.as_str()) } } - } else { - match ty.peel_refs().kind() { - ty::FnPtr(_) => Some("function pointers"), - ty::RawPtr(_) => Some("raw pointers"), - _ => None, - } }; if let Some(unsupported_type) = err { if is_ptr { @@ -330,7 +323,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { ), ) .note("the only supported types are integers, `bool` and `char`") - .help("more complex types are supported with `#[feature(const_generics)]`") + .help("more complex types are supported with `#![feature(const_generics)]`") .emit() } }; @@ -539,7 +532,7 @@ fn check_type_defn<'tcx, F>( fcx.register_predicate(traits::Obligation::new( cause, fcx.param_env, - ty::PredicateAtom::ConstEvaluatable( + ty::PredicateKind::ConstEvaluatable( ty::WithOptConstParam::unknown(discr_def_id.to_def_id()), discr_substs, ) @@ -785,7 +778,7 @@ fn check_where_clauses<'tcx, 'fcx>( } GenericParamDefKind::Const => { - // FIXME(const_generics:defaults) + // FIXME(const_generics_defaults) fcx.tcx.mk_param_from_def(param) } } diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 7c9cfe69fc..4d18b2cb3f 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -348,7 +348,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let min_list_wb = min_list .iter() .map(|captured_place| { - let locatable = captured_place.info.expr_id.unwrap_or( + let locatable = captured_place.info.path_expr_id.unwrap_or( self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local()), ); @@ -384,9 +384,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); let common_hir_owner = fcx_typeck_results.hir_owner; - for (&id, &origin) in fcx_typeck_results.closure_kind_origins().iter() { - let hir_id = hir::HirId { owner: common_hir_owner, local_id: id }; - self.typeck_results.closure_kind_origins_mut().insert(hir_id, origin); + for (id, origin) in fcx_typeck_results.closure_kind_origins().iter() { + let hir_id = hir::HirId { owner: common_hir_owner, local_id: *id }; + let place_span = origin.0; + let place = self.resolve(origin.1.clone(), &place_span); + self.typeck_results.closure_kind_origins_mut().insert(hir_id, (place_span, place)); } } @@ -648,7 +650,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } -trait Locatable { +crate trait Locatable { fn to_span(&self, tcx: TyCtxt<'_>) -> Span; } @@ -666,7 +668,7 @@ impl Locatable for hir::HirId { /// The Resolver. This is the type folding engine that detects /// unresolved types and so forth. -struct Resolver<'cx, 'tcx> { +crate struct Resolver<'cx, 'tcx> { tcx: TyCtxt<'tcx>, infcx: &'cx InferCtxt<'cx, 'tcx>, span: &'cx dyn Locatable, @@ -677,7 +679,7 @@ struct Resolver<'cx, 'tcx> { } impl<'cx, 'tcx> Resolver<'cx, 'tcx> { - fn new( + crate fn new( fcx: &'cx FnCtxt<'cx, 'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, @@ -692,6 +694,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { Some(self.body.id()), self.span.to_span(self.tcx), t.into(), + vec![], E0282, ) .emit(); @@ -705,6 +708,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { Some(self.body.id()), self.span.to_span(self.tcx), c.into(), + vec![], E0282, ) .emit(); diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs index 89270fb6c7..6726b9b4a4 100644 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ b/compiler/rustc_typeck/src/coherence/builtin.rs @@ -55,7 +55,7 @@ fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did); let sp = match tcx.hir().expect_item(impl_hir_id).kind { - ItemKind::Impl { self_ty, .. } => self_ty.span, + ItemKind::Impl(ref impl_) => impl_.self_ty.span, _ => bug!("expected Drop impl item"), }; @@ -80,7 +80,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { Ok(()) => {} Err(CopyImplementationError::InfrigingFields(fields)) => { let item = tcx.hir().expect_item(impl_hir_id); - let span = if let ItemKind::Impl { of_trait: Some(ref tr), .. } = item.kind { + let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(ref tr), .. }) = item.kind { tr.path.span } else { span @@ -100,7 +100,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { Err(CopyImplementationError::NotAnAdt) => { let item = tcx.hir().expect_item(impl_hir_id); let span = - if let ItemKind::Impl { self_ty, .. } = item.kind { self_ty.span } else { span }; + if let ItemKind::Impl(ref impl_) = item.kind { impl_.self_ty.span } else { span }; tcx.sess.emit_err(CopyImplOnNonAdt { span }); } @@ -453,7 +453,9 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI return err_info; } else if diff_fields.len() > 1 { let item = tcx.hir().expect_item(impl_hir_id); - let span = if let ItemKind::Impl { of_trait: Some(ref t), .. } = item.kind { + let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(ref t), .. }) = + item.kind + { t.path.span } else { tcx.hir().span(impl_hir_id) diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs index 0c1578498b..8a500852a0 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs @@ -13,7 +13,6 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::{self, CrateInherentImpls, TyCtxt}; -use rustc_ast as ast; use rustc_span::Span; /// On-demand query: yields a map containing all types mapped to their inherent impls. @@ -45,7 +44,9 @@ struct InherentCollect<'tcx> { impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { fn visit_item(&mut self, item: &hir::Item<'_>) { let (ty, assoc_items) = match item.kind { - hir::ItemKind::Impl { of_trait: None, ref self_ty, items, .. } => (self_ty, items), + hir::ItemKind::Impl(hir::Impl { of_trait: None, ref self_ty, items, .. }) => { + (self_ty, items) + } _ => return, }; @@ -176,7 +177,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Int(ast::IntTy::I8) => { + ty::Int(ty::IntTy::I8) => { self.check_primitive_impl( def_id, lang_items.i8_impl(), @@ -187,7 +188,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Int(ast::IntTy::I16) => { + ty::Int(ty::IntTy::I16) => { self.check_primitive_impl( def_id, lang_items.i16_impl(), @@ -198,7 +199,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Int(ast::IntTy::I32) => { + ty::Int(ty::IntTy::I32) => { self.check_primitive_impl( def_id, lang_items.i32_impl(), @@ -209,7 +210,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Int(ast::IntTy::I64) => { + ty::Int(ty::IntTy::I64) => { self.check_primitive_impl( def_id, lang_items.i64_impl(), @@ -220,7 +221,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Int(ast::IntTy::I128) => { + ty::Int(ty::IntTy::I128) => { self.check_primitive_impl( def_id, lang_items.i128_impl(), @@ -231,7 +232,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Int(ast::IntTy::Isize) => { + ty::Int(ty::IntTy::Isize) => { self.check_primitive_impl( def_id, lang_items.isize_impl(), @@ -242,7 +243,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Uint(ast::UintTy::U8) => { + ty::Uint(ty::UintTy::U8) => { self.check_primitive_impl( def_id, lang_items.u8_impl(), @@ -253,7 +254,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Uint(ast::UintTy::U16) => { + ty::Uint(ty::UintTy::U16) => { self.check_primitive_impl( def_id, lang_items.u16_impl(), @@ -264,7 +265,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Uint(ast::UintTy::U32) => { + ty::Uint(ty::UintTy::U32) => { self.check_primitive_impl( def_id, lang_items.u32_impl(), @@ -275,7 +276,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Uint(ast::UintTy::U64) => { + ty::Uint(ty::UintTy::U64) => { self.check_primitive_impl( def_id, lang_items.u64_impl(), @@ -286,7 +287,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Uint(ast::UintTy::U128) => { + ty::Uint(ty::UintTy::U128) => { self.check_primitive_impl( def_id, lang_items.u128_impl(), @@ -297,7 +298,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Uint(ast::UintTy::Usize) => { + ty::Uint(ty::UintTy::Usize) => { self.check_primitive_impl( def_id, lang_items.usize_impl(), @@ -308,7 +309,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Float(ast::FloatTy::F32) => { + ty::Float(ty::FloatTy::F32) => { self.check_primitive_impl( def_id, lang_items.f32_impl(), @@ -319,7 +320,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { assoc_items, ); } - ty::Float(ast::FloatTy::F64) => { + ty::Float(ty::FloatTy::F64) => { self.check_primitive_impl( def_id, lang_items.f64_impl(), diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index 253dcf06e0..9333aac601 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -26,7 +26,10 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { fn visit_item(&mut self, item: &hir::Item<'_>) { let def_id = self.tcx.hir().local_def_id(item.hir_id); // "Trait" impl - if let hir::ItemKind::Impl { generics, of_trait: Some(ref tr), self_ty, .. } = &item.kind { + if let hir::ItemKind::Impl(hir::Impl { + generics, of_trait: Some(ref tr), self_ty, .. + }) = &item.kind + { debug!( "coherence2::orphan check: trait impl {}", self.tcx.hir().node_to_string(item.hir_id) diff --git a/compiler/rustc_typeck/src/coherence/unsafety.rs b/compiler/rustc_typeck/src/coherence/unsafety.rs index 2d9128e7dc..3a290b7756 100644 --- a/compiler/rustc_typeck/src/coherence/unsafety.rs +++ b/compiler/rustc_typeck/src/coherence/unsafety.rs @@ -86,8 +86,13 @@ impl UnsafetyChecker<'tcx> { impl ItemLikeVisitor<'v> for UnsafetyChecker<'tcx> { fn visit_item(&mut self, item: &'v hir::Item<'v>) { - if let hir::ItemKind::Impl { unsafety, polarity, ref generics, .. } = item.kind { - self.check_unsafety_coherence(item, Some(generics), unsafety, polarity); + if let hir::ItemKind::Impl(ref impl_) = item.kind { + self.check_unsafety_coherence( + item, + Some(&impl_.generics), + impl_.unsafety, + impl_.polarity, + ); } } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index bc6b2037c1..c6cc54d712 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -50,8 +50,6 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; -use std::ops::ControlFlow; - mod item_bounds; mod type_of; @@ -156,10 +154,10 @@ crate fn placeholder_type_error( if let Some(span) = span { sugg.push((span, format!("<{}>", type_name))); } - } else if let Some(arg) = generics.iter().find(|arg| match arg.name { - hir::ParamName::Plain(Ident { name: kw::Underscore, .. }) => true, - _ => false, - }) { + } else if let Some(arg) = generics + .iter() + .find(|arg| matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. }))) + { // Account for `_` already present in cases like `struct S<_>(_);` and suggest // `struct S(T);` instead of `struct S<_, T>(T);`. sugg.push((arg.span, (*type_name).to_string())); @@ -189,7 +187,7 @@ fn reject_placeholder_type_signatures_in_item(tcx: TyCtxt<'tcx>, item: &'tcx hir | hir::ItemKind::Enum(_, generics) | hir::ItemKind::TraitAlias(generics, _) | hir::ItemKind::Trait(_, _, generics, ..) - | hir::ItemKind::Impl { generics, .. } + | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Struct(_, generics) => (generics, true), hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }) | hir::ItemKind::TyAlias(_, generics) => (generics, false), @@ -228,7 +226,7 @@ impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { hir::GenericParamKind::Const { .. } => { let def_id = self.tcx.hir().local_def_id(param.hir_id); self.tcx.ensure().type_of(def_id); - // FIXME(const_generics:defaults) + // FIXME(const_generics_defaults) } } } @@ -333,6 +331,11 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { span: Span, ) -> &'tcx Const<'tcx> { bad_placeholder_type(self.tcx(), vec![span]).emit(); + // Typeck doesn't expect erased regions to be returned from `type_of`. + let ty = self.tcx.fold_regions(ty, &mut false, |r, _| match r { + ty::ReErased => self.tcx.lifetimes.re_static, + _ => r, + }); self.tcx().const_error(ty) } @@ -531,7 +534,7 @@ fn type_param_predicates( Node::Item(item) => { match item.kind { ItemKind::Fn(.., ref generics, _) - | ItemKind::Impl { ref generics, .. } + | ItemKind::Impl(hir::Impl { ref generics, .. }) | ItemKind::TyAlias(_, ref generics) | ItemKind::OpaqueTy(OpaqueTy { ref generics, impl_trait_fn: None, .. }) | ItemKind::Enum(_, ref generics) @@ -562,8 +565,8 @@ fn type_param_predicates( let extra_predicates = extend.into_iter().chain( icx.type_parameter_bounds_in_generics(ast_generics, param_id, ty, OnlySelfBounds(true)) .into_iter() - .filter(|(predicate, _)| match predicate.skip_binders() { - ty::PredicateAtom::Trait(data, _) => data.self_ty().is_param(index), + .filter(|(predicate, _)| match predicate.kind().skip_binder() { + ty::PredicateKind::Trait(data, _) => data.self_ty().is_param(index), _ => false, }), ); @@ -1027,7 +1030,7 @@ fn super_predicates_of(tcx: TyCtxt<'_>, trait_def_id: DefId) -> ty::GenericPredi // which will, in turn, reach indirect supertraits. for &(pred, span) in superbounds { debug!("superbound: {:?}", pred); - if let ty::PredicateAtom::Trait(bound, _) = pred.skip_binders() { + if let ty::PredicateKind::Trait(bound, _) = pred.kind().skip_binder() { tcx.at(span).super_predicates_of(bound.def_id()); } } @@ -1260,7 +1263,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { // used with const generics, e.g. `Foo<{N+1}>`, can work at all. // // Note that we do not supply the parent generics when using - // `feature(min_const_generics)`. + // `min_const_generics`. Some(parent_def_id.to_def_id()) } else { let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); @@ -1310,7 +1313,8 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { Node::Item(item) => { match item.kind { - ItemKind::Fn(.., ref generics, _) | ItemKind::Impl { ref generics, .. } => generics, + ItemKind::Fn(.., ref generics, _) + | ItemKind::Impl(hir::Impl { ref generics, .. }) => generics, ItemKind::TyAlias(_, ref generics) | ItemKind::Enum(_, ref generics) @@ -1497,13 +1501,11 @@ fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool { Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty), OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args), Path(hir::QPath::TypeRelative(ty, segment)) => { - is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.generic_args().args) + is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.args().args) } Path(hir::QPath::Resolved(ty_opt, hir::Path { segments, .. })) => { ty_opt.map_or(false, is_suggestable_infer_ty) - || segments - .iter() - .any(|segment| are_suggestable_generic_args(segment.generic_args().args)) + || segments.iter().any(|segment| are_suggestable_generic_args(segment.args().args)) } _ => false, } @@ -1539,19 +1541,41 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { match get_infer_ret_ty(&sig.decl.output) { Some(ty) => { let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id]; + // Typeck doesn't expect erased regions to be returned from `type_of`. + let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match r { + ty::ReErased => tcx.lifetimes.re_static, + _ => r, + }); + let mut visitor = PlaceholderHirTyCollector::default(); visitor.visit_ty(ty); let mut diag = bad_placeholder_type(tcx, visitor.0); let ret_ty = fn_sig.output(); if ret_ty != tcx.ty_error() { - diag.span_suggestion( - ty.span, - "replace with the correct return type", - ret_ty.to_string(), - Applicability::MaybeIncorrect, - ); + if !ret_ty.is_closure() { + let ret_ty_str = match ret_ty.kind() { + // Suggest a function pointer return type instead of a unique function definition + // (e.g. `fn() -> i32` instead of `fn() -> i32 { f }`, the latter of which is invalid + // syntax) + ty::FnDef(..) => ret_ty.fn_sig(tcx).to_string(), + _ => ret_ty.to_string(), + }; + diag.span_suggestion( + ty.span, + "replace with the correct return type", + ret_ty_str, + Applicability::MaybeIncorrect, + ); + } else { + // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds + // to prevent the user from getting a papercut while trying to use the unique closure + // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`). + diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); + diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html"); + } } diag.emit(); + ty::Binder::bind(fn_sig) } None => AstConv::ty_of_fn( @@ -1623,7 +1647,7 @@ fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); match tcx.hir().expect_item(hir_id).kind { - hir::ItemKind::Impl { ref of_trait, .. } => of_trait.as_ref().map(|ast_trait_ref| { + hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| { let selfty = tcx.type_of(def_id); AstConv::instantiate_mono_trait_ref(&icx, ast_trait_ref, selfty) }), @@ -1636,29 +1660,39 @@ fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity { let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); let item = tcx.hir().expect_item(hir_id); match &item.kind { - hir::ItemKind::Impl { polarity: hir::ImplPolarity::Negative(span), of_trait, .. } => { + hir::ItemKind::Impl(hir::Impl { + polarity: hir::ImplPolarity::Negative(span), + of_trait, + .. + }) => { if is_rustc_reservation { - let span = span.to(of_trait.as_ref().map(|t| t.path.span).unwrap_or(*span)); + let span = span.to(of_trait.as_ref().map_or(*span, |t| t.path.span)); tcx.sess.span_err(span, "reservation impls can't be negative"); } ty::ImplPolarity::Negative } - hir::ItemKind::Impl { polarity: hir::ImplPolarity::Positive, of_trait: None, .. } => { + hir::ItemKind::Impl(hir::Impl { + polarity: hir::ImplPolarity::Positive, + of_trait: None, + .. + }) => { if is_rustc_reservation { tcx.sess.span_err(item.span, "reservation impls can't be inherent"); } ty::ImplPolarity::Positive } - hir::ItemKind::Impl { - polarity: hir::ImplPolarity::Positive, of_trait: Some(_), .. - } => { + hir::ItemKind::Impl(hir::Impl { + polarity: hir::ImplPolarity::Positive, + of_trait: Some(_), + .. + }) => { if is_rustc_reservation { ty::ImplPolarity::Reservation } else { ty::ImplPolarity::Positive } } - ref item => bug!("impl_polarity: {:?} not an impl", item), + item => bug!("impl_polarity: {:?} not an impl", item), } } @@ -1752,8 +1786,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP const NO_GENERICS: &hir::Generics<'_> = &hir::Generics::empty(); // We use an `IndexSet` to preserves order of insertion. - // Preserving the order of insertion is important here so as not to break - // compile-fail UI tests. + // Preserving the order of insertion is important here so as not to break UI tests. let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default(); let ast_generics = match node { @@ -1763,11 +1796,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP Node::Item(item) => { match item.kind { - ItemKind::Impl { defaultness, ref generics, .. } => { - if defaultness.is_default() { + ItemKind::Impl(ref impl_) => { + if impl_.defaultness.is_default() { is_default_impl_trait = tcx.impl_trait_ref(def_id); } - generics + &impl_.generics } ItemKind::Fn(.., ref generics, _) | ItemKind::TyAlias(_, ref generics) @@ -1906,7 +1939,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP let where_clause = &ast_generics.where_clause; for predicate in where_clause.predicates { match predicate { - &hir::WherePredicate::BoundPredicate(ref bound_pred) => { + hir::WherePredicate::BoundPredicate(bound_pred) => { let ty = icx.to_ty(&bound_pred.bounded_ty); // Keep the type around in a dummy predicate, in case of no bounds. @@ -1923,19 +1956,16 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } else { let span = bound_pred.bounded_ty.span; let re_root_empty = tcx.lifetimes.re_root_empty; - let predicate = ty::Binder::bind(ty::PredicateAtom::TypeOutlives( + let predicate = ty::Binder::bind(ty::PredicateKind::TypeOutlives( ty::OutlivesPredicate(ty, re_root_empty), )); - predicates.insert(( - predicate.potentially_quantified(tcx, ty::PredicateKind::ForAll), - span, - )); + predicates.insert((predicate.to_predicate(tcx), span)); } } for bound in bound_pred.bounds.iter() { match bound { - &hir::GenericBound::Trait(ref poly_trait_ref, modifier) => { + hir::GenericBound::Trait(poly_trait_ref, modifier) => { let constness = match modifier { hir::TraitBoundModifier::MaybeConst => hir::Constness::NotConst, hir::TraitBoundModifier::None => constness, @@ -1945,7 +1975,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP let mut bounds = Bounds::default(); let _ = AstConv::instantiate_poly_trait_ref( &icx, - poly_trait_ref, + &poly_trait_ref, constness, ty, &mut bounds, @@ -1967,13 +1997,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP predicates.extend(bounds.predicates(tcx, ty)); } - &hir::GenericBound::Outlives(ref lifetime) => { + hir::GenericBound::Outlives(lifetime) => { let region = AstConv::ast_region_to_region(&icx, lifetime, None); predicates.insert(( - ty::Binder::bind(ty::PredicateAtom::TypeOutlives( + ty::Binder::bind(ty::PredicateKind::TypeOutlives( ty::OutlivesPredicate(ty, region), )) - .potentially_quantified(tcx, ty::PredicateKind::ForAll), + .to_predicate(tcx), lifetime.span, )); } @@ -1981,7 +2011,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } } - &hir::WherePredicate::RegionPredicate(ref region_pred) => { + hir::WherePredicate::RegionPredicate(region_pred) => { let r1 = AstConv::ast_region_to_region(&icx, ®ion_pred.lifetime, None); predicates.extend(region_pred.bounds.iter().map(|bound| { let (r2, span) = match bound { @@ -1990,14 +2020,14 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } _ => bug!(), }; - let pred = ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate(r1, r2)) + let pred = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r1, r2)) .to_predicate(icx.tcx); (pred, span) })) } - &hir::WherePredicate::EqPredicate(..) => { + hir::WherePredicate::EqPredicate(..) => { // FIXME(#20041) } } @@ -2055,43 +2085,11 @@ fn const_evaluatable_predicates_of<'tcx>( if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val { let span = self.tcx.hir().span(c.hir_id); self.preds.insert(( - ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx), + ty::PredicateKind::ConstEvaluatable(def, substs).to_predicate(self.tcx), span, )); } } - - // Look into `TyAlias`. - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - use ty::fold::{TypeFoldable, TypeVisitor}; - struct TyAliasVisitor<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - preds: &'a mut FxIndexSet<(ty::Predicate<'tcx>, Span)>, - span: Span, - } - - impl<'a, 'tcx> TypeVisitor<'tcx> for TyAliasVisitor<'a, 'tcx> { - fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> ControlFlow { - if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val { - self.preds.insert(( - ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx), - self.span, - )); - } - ControlFlow::CONTINUE - } - } - - if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind { - if let Res::Def(DefKind::TyAlias, def_id) = path.res { - let mut visitor = - TyAliasVisitor { tcx: self.tcx, preds: &mut self.preds, span: path.span }; - self.tcx.type_of(def_id).visit_with(&mut visitor); - } - } - - intravisit::walk_ty(self, ty) - } } let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); @@ -2099,14 +2097,14 @@ fn const_evaluatable_predicates_of<'tcx>( let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() }; if let hir::Node::Item(item) = node { - if let hir::ItemKind::Impl { ref of_trait, ref self_ty, .. } = item.kind { - if let Some(of_trait) = of_trait { + if let hir::ItemKind::Impl(ref impl_) = item.kind { + if let Some(of_trait) = &impl_.of_trait { debug!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id); collector.visit_trait_ref(of_trait); } debug!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id); - collector.visit_ty(self_ty); + collector.visit_ty(impl_.self_ty); } } @@ -2160,12 +2158,12 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat .predicates .iter() .copied() - .filter(|(pred, _)| match pred.skip_binders() { - ty::PredicateAtom::Trait(tr, _) => !is_assoc_item_ty(tr.self_ty()), - ty::PredicateAtom::Projection(proj) => { + .filter(|(pred, _)| match pred.kind().skip_binder() { + ty::PredicateKind::Trait(tr, _) => !is_assoc_item_ty(tr.self_ty()), + ty::PredicateKind::Projection(proj) => { !is_assoc_item_ty(proj.projection_ty.self_ty()) } - ty::PredicateAtom::TypeOutlives(outlives) => !is_assoc_item_ty(outlives.0), + ty::PredicateKind::TypeOutlives(outlives) => !is_assoc_item_ty(outlives.0), _ => true, }) .collect(); @@ -2194,7 +2192,8 @@ fn projection_ty_from_predicates( let (ty_def_id, item_def_id) = key; let mut projection_ty = None; for (predicate, _) in tcx.predicates_of(ty_def_id).predicates { - if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() { + if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() + { if item_def_id == projection_predicate.projection_ty.item_def_id { projection_ty = Some(projection_predicate.projection_ty); break; @@ -2241,7 +2240,7 @@ fn predicates_from_bound<'tcx>( } hir::GenericBound::Outlives(ref lifetime) => { let region = astconv.ast_region_to_region(lifetime, None); - let pred = ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(param_ty, region)) + let pred = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(param_ty, region)) .to_predicate(astconv.tcx()); vec![(pred, lifetime.span)] } @@ -2938,7 +2937,7 @@ fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { let parent_id = tcx.hir().get_parent_item(hir_id); let parent_item = tcx.hir().expect_item(parent_id); - if let hir::ItemKind::Impl { of_trait: Some(_), .. } = parent_item.kind { + if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = parent_item.kind { tcx.sess .struct_span_err( attr_span, diff --git a/compiler/rustc_typeck/src/collect/item_bounds.rs b/compiler/rustc_typeck/src/collect/item_bounds.rs index e596dd1a39..537a583289 100644 --- a/compiler/rustc_typeck/src/collect/item_bounds.rs +++ b/compiler/rustc_typeck/src/collect/item_bounds.rs @@ -36,13 +36,14 @@ fn associated_type_bounds<'tcx>( let trait_def_id = tcx.associated_item(assoc_item_def_id).container.id(); let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id.expect_local()); - let bounds_from_parent = - trait_predicates.predicates.iter().copied().filter(|(pred, _)| match pred.skip_binders() { - ty::PredicateAtom::Trait(tr, _) => tr.self_ty() == item_ty, - ty::PredicateAtom::Projection(proj) => proj.projection_ty.self_ty() == item_ty, - ty::PredicateAtom::TypeOutlives(outlives) => outlives.0 == item_ty, + let bounds_from_parent = trait_predicates.predicates.iter().copied().filter(|(pred, _)| { + match pred.kind().skip_binder() { + ty::PredicateKind::Trait(tr, _) => tr.self_ty() == item_ty, + ty::PredicateKind::Projection(proj) => proj.projection_ty.self_ty() == item_ty, + ty::PredicateKind::TypeOutlives(outlives) => outlives.0 == item_ty, _ => false, - }); + } + }); let all_bounds = tcx .arena diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 88ba5788b0..e4eabca9c3 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -6,7 +6,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit; use rustc_hir::intravisit::Visitor; -use rustc_hir::Node; +use rustc_hir::{HirId, Node}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::util::IntTypeExt; @@ -22,7 +22,6 @@ use super::{bad_placeholder_type, is_suggestable_infer_ty}; /// This should be called using the query `tcx.opt_const_param_of`. pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { use hir::*; - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); if let Node::AnonConst(_) = tcx.hir().get(hir_id) { @@ -62,9 +61,9 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< } Node::Ty(&Ty { kind: TyKind::Path(_), .. }) - | Node::Expr(&Expr { kind: ExprKind::Struct(..), .. }) - | Node::Expr(&Expr { kind: ExprKind::Path(_), .. }) - | Node::TraitRef(..) => { + | Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. }) + | Node::TraitRef(..) + | Node::Pat(_) => { let path = match parent_node { Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) | Node::TraitRef(&TraitRef { path, .. }) => &*path, @@ -79,6 +78,20 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< let _tables = tcx.typeck(body_owner); &*path } + Node::Pat(pat) => { + if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) { + path + } else { + tcx.sess.delay_span_bug( + tcx.def_span(def_id), + &format!( + "unable to find const parent for {} in pat {:?}", + hir_id, pat + ), + ); + return None; + } + } _ => { tcx.sess.delay_span_bug( tcx.def_span(def_id), @@ -91,7 +104,6 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< // We've encountered an `AnonConst` in some path, so we need to // figure out which generic parameter it corresponds to and return // the relevant type. - let (arg_index, segment) = path .segments .iter() @@ -144,6 +156,34 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< } } +fn get_path_containing_arg_in_pat<'hir>( + pat: &'hir hir::Pat<'hir>, + arg_id: HirId, +) -> Option<&'hir hir::Path<'hir>> { + use hir::*; + + let is_arg_in_path = |p: &hir::Path<'_>| { + p.segments + .iter() + .filter_map(|seg| seg.args) + .flat_map(|args| args.args) + .any(|arg| arg.id() == arg_id) + }; + let mut arg_path = None; + pat.walk(|pat| match pat.kind { + PatKind::Struct(QPath::Resolved(_, path), _, _) + | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) + | PatKind::Path(QPath::Resolved(_, path)) + if is_arg_in_path(path) => + { + arg_path = Some(path); + false + } + _ => true, + }); + arg_path +} + pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { let def_id = def_id.expect_local(); use rustc_hir::*; @@ -203,9 +243,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { icx.to_ty(ty) } } - ItemKind::TyAlias(ref self_ty, _) | ItemKind::Impl { ref self_ty, .. } => { - icx.to_ty(self_ty) - } + ItemKind::TyAlias(ref self_ty, _) + | ItemKind::Impl(hir::Impl { ref self_ty, .. }) => icx.to_ty(self_ty), ItemKind::Fn(..) => { let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); tcx.mk_fn_def(def_id.to_def_id(), substs) diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs index e389fd4d9f..95670b9bdb 100644 --- a/compiler/rustc_typeck/src/constrained_generic_params.rs +++ b/compiler/rustc_typeck/src/constrained_generic_params.rs @@ -183,7 +183,8 @@ pub fn setup_constraining_predicates<'tcx>( for j in i..predicates.len() { // Note that we don't have to care about binders here, // as the impl trait ref never contains any late-bound regions. - if let ty::PredicateAtom::Projection(projection) = predicates[j].0.skip_binders() { + if let ty::PredicateKind::Projection(projection) = predicates[j].0.kind().skip_binder() + { // Special case: watch out for some kind of sneaky attempt // to project out an associated type defined by this very // trait. diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index ce9fd5575c..bd2c266d93 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -219,6 +219,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.consume_exprs(exprs); } + hir::ExprKind::If(ref cond_expr, ref then_expr, ref opt_else_expr) => { + self.consume_expr(&cond_expr); + self.consume_expr(&then_expr); + if let Some(ref else_expr) = *opt_else_expr { + self.consume_expr(&else_expr); + } + } + hir::ExprKind::Match(ref discr, arms, _) => { let discr_place = return_if_err!(self.mc.cat_expr(&discr)); self.borrow_expr(&discr, ty::ImmBorrow); @@ -281,7 +289,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { | hir::ExprKind::ConstBlock(..) | hir::ExprKind::Err => {} - hir::ExprKind::Loop(ref blk, _, _) => { + hir::ExprKind::Loop(ref blk, ..) => { self.walk_block(blk); } @@ -595,10 +603,10 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let upvars = self.tcx().upvars_mentioned(self.body_owner); // For purposes of this function, generator and closures are equivalent. - let body_owner_is_closure = match self.tcx().type_of(self.body_owner.to_def_id()).kind() { - ty::Closure(..) | ty::Generator(..) => true, - _ => false, - }; + let body_owner_is_closure = matches!( + self.tcx().type_of(self.body_owner.to_def_id()).kind(), + ty::Closure(..) | ty::Generator(..) + ); if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id) { @@ -622,7 +630,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { PlaceBase::Local(*var_hir_id) }; let place_with_id = PlaceWithHirId::new( - capture_info.expr_id.unwrap_or(closure_expr.hir_id), + capture_info.path_expr_id.unwrap_or(closure_expr.hir_id), place.base_ty, place_base, place.projections.clone(), diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index 14daa97c2c..0bdcbaac0e 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -80,10 +80,10 @@ struct ImplWfCheck<'tcx> { impl ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - if let hir::ItemKind::Impl { ref items, .. } = item.kind { + if let hir::ItemKind::Impl(ref impl_) = item.kind { let impl_def_id = self.tcx.hir().local_def_id(item.hir_id); - enforce_impl_params_are_constrained(self.tcx, impl_def_id, items); - enforce_impl_items_are_distinct(self.tcx, items); + enforce_impl_params_are_constrained(self.tcx, impl_def_id, impl_.items); + enforce_impl_items_are_distinct(self.tcx, impl_.items); if self.min_specialization { check_min_specialization(self.tcx, impl_def_id.to_def_id(), item.span); } diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index 5db9ff9524..505d9a59d9 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -198,7 +198,7 @@ fn unconstrained_parent_impl_substs<'tcx>( // the functions in `cgp` add the constrained parameters to a list of // unconstrained parameters. for (predicate, _) in impl_generic_predicates.predicates.iter() { - if let ty::PredicateAtom::Projection(proj) = predicate.skip_binders() { + if let ty::PredicateKind::Projection(proj) = predicate.kind().skip_binder() { let projection_ty = proj.projection_ty; let projected_ty = proj.ty; @@ -360,13 +360,13 @@ fn check_predicates<'tcx>( fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, span: Span) { debug!("can_specialize_on(predicate = {:?})", predicate); - match predicate.skip_binders() { + match predicate.kind().skip_binder() { // Global predicates are either always true or always false, so we // are fine to specialize on. _ if predicate.is_global() => (), // We allow specializing on explicitly marked traits with no associated // items. - ty::PredicateAtom::Trait(pred, hir::Constness::NotConst) => { + ty::PredicateKind::Trait(pred, hir::Constness::NotConst) => { if !matches!( trait_predicate_kind(tcx, predicate), Some(TraitSpecializationKind::Marker) @@ -393,20 +393,20 @@ fn trait_predicate_kind<'tcx>( tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, ) -> Option { - match predicate.skip_binders() { - ty::PredicateAtom::Trait(pred, hir::Constness::NotConst) => { + match predicate.kind().skip_binder() { + ty::PredicateKind::Trait(pred, hir::Constness::NotConst) => { Some(tcx.trait_def(pred.def_id()).specialization_kind) } - ty::PredicateAtom::Trait(_, hir::Constness::Const) - | ty::PredicateAtom::RegionOutlives(_) - | ty::PredicateAtom::TypeOutlives(_) - | ty::PredicateAtom::Projection(_) - | ty::PredicateAtom::WellFormed(_) - | ty::PredicateAtom::Subtype(_) - | ty::PredicateAtom::ObjectSafe(_) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, + ty::PredicateKind::Trait(_, hir::Constness::Const) + | ty::PredicateKind::RegionOutlives(_) + | ty::PredicateKind::TypeOutlives(_) + | ty::PredicateKind::Projection(_) + | ty::PredicateKind::WellFormed(_) + | ty::PredicateKind::Subtype(_) + | ty::PredicateKind::ObjectSafe(_) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } } diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index dde4a62ffb..fd44bafab6 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -7,9 +7,9 @@ The type checker is responsible for: 1. Determining the type of each expression. 2. Resolving methods and traits. 3. Guaranteeing that most type rules are met. ("Most?", you say, "why most?" - Well, dear reader, read on) + Well, dear reader, read on.) -The main entry point is `check_crate()`. Type checking operates in +The main entry point is [`check_crate()`]. Type checking operates in several major phases: 1. The collect phase first passes over all items and determines their @@ -25,7 +25,7 @@ several major phases: containing function). Inference is used to supply types wherever they are unknown. The actual checking of a function itself has several phases (check, regionck, writeback), as discussed in the - documentation for the `check` module. + documentation for the [`check`] module. The type checker is defined into various submodules which are documented independently: @@ -56,10 +56,10 @@ This API is completely unstable and subject to change. */ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(array_value_iter)] #![feature(bool_to_option)] #![feature(box_syntax)] #![feature(crate_visibility_modifier)] +#![feature(format_args_capture)] #![feature(in_band_lifetimes)] #![feature(is_sorted)] #![feature(nll)] diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index 9992094117..fef52a3f87 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -364,6 +364,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) | hir::ExprKind::Array(..) + | hir::ExprKind::If(..) | hir::ExprKind::Tup(..) | hir::ExprKind::Binary(..) | hir::ExprKind::Block(..) @@ -459,7 +460,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { kind: ProjectionKind, ) -> PlaceWithHirId<'tcx> { let mut projections = base_place.place.projections; - projections.push(Projection { kind: kind, ty: ty }); + projections.push(Projection { kind, ty }); let ret = PlaceWithHirId::new( node.hir_id(), base_place.place.base_ty, @@ -653,9 +654,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // Then we see that to get the same result, we must start with // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. - for _ in - 0..self.typeck_results.pat_adjustments().get(pat.hir_id).map(|v| v.len()).unwrap_or(0) - { + for _ in 0..self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) { debug!("cat_pattern: applying adjustment to place_with_id={:?}", place_with_id); place_with_id = self.cat_deref(pat, place_with_id)?; } diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_typeck/src/outlives/explicit.rs index ae336ccca4..6e5be87928 100644 --- a/compiler/rustc_typeck/src/outlives/explicit.rs +++ b/compiler/rustc_typeck/src/outlives/explicit.rs @@ -29,8 +29,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { // process predicates and convert to `RequiredPredicates` entry, see below for &(predicate, span) in predicates.predicates { - match predicate.skip_binders() { - ty::PredicateAtom::TypeOutlives(OutlivesPredicate(ref ty, ref reg)) => { + match predicate.kind().skip_binder() { + ty::PredicateKind::TypeOutlives(OutlivesPredicate(ref ty, ref reg)) => { insert_outlives_predicate( tcx, (*ty).into(), @@ -40,7 +40,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { ) } - ty::PredicateAtom::RegionOutlives(OutlivesPredicate(ref reg1, ref reg2)) => { + ty::PredicateKind::RegionOutlives(OutlivesPredicate(ref reg1, ref reg2)) => { insert_outlives_predicate( tcx, (*reg1).into(), @@ -50,15 +50,15 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { ) } - ty::PredicateAtom::Trait(..) - | ty::PredicateAtom::Projection(..) - | ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => (), + ty::PredicateKind::Trait(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => (), } } diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs index 3d0635e3fe..02008e180b 100644 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs @@ -99,7 +99,7 @@ impl<'cx, 'tcx> ItemLikeVisitor<'tcx> for InferVisitor<'cx, 'tcx> { // we walk the crates again and re-calculate predicates for all // items. let item_predicates_len: usize = - self.global_inferred_outlives.get(&item_did.to_def_id()).map(|p| p.len()).unwrap_or(0); + self.global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.len()); if item_required_predicates.len() > item_predicates_len { *self.predicates_added = true; self.global_inferred_outlives.insert(item_did.to_def_id(), item_required_predicates); diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs index b1f79331d5..e94b8450bf 100644 --- a/compiler/rustc_typeck/src/outlives/mod.rs +++ b/compiler/rustc_typeck/src/outlives/mod.rs @@ -30,13 +30,9 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate if tcx.has_attr(item_def_id, sym::rustc_outlives) { let mut pred: Vec = predicates .iter() - .map(|(out_pred, _)| match out_pred.kind() { - ty::PredicateKind::Atom(ty::PredicateAtom::RegionOutlives(p)) => { - p.to_string() - } - ty::PredicateKind::Atom(ty::PredicateAtom::TypeOutlives(p)) => { - p.to_string() - } + .map(|(out_pred, _)| match out_pred.kind().skip_binder() { + ty::PredicateKind::RegionOutlives(p) => p.to_string(), + ty::PredicateKind::TypeOutlives(p) => p.to_string(), err => bug!("unexpected predicate {:?}", err), }) .collect(); @@ -89,12 +85,12 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CratePredica |(ty::OutlivesPredicate(kind1, region2), &span)| { match kind1.unpack() { GenericArgKind::Type(ty1) => Some(( - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty1, region2)) + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty1, region2)) .to_predicate(tcx), span, )), GenericArgKind::Lifetime(region1) => Some(( - ty::PredicateAtom::RegionOutlives(ty::OutlivesPredicate( + ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( region1, region2, )) .to_predicate(tcx), diff --git a/compiler/rustc_typeck/src/structured_errors.rs b/compiler/rustc_typeck/src/structured_errors.rs index 83125a3e2f..04d04304e7 100644 --- a/compiler/rustc_typeck/src/structured_errors.rs +++ b/compiler/rustc_typeck/src/structured_errors.rs @@ -1,149 +1,36 @@ -use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId}; -use rustc_middle::ty::{Ty, TypeFoldable}; +mod missing_cast_for_variadic_arg; +mod sized_unsized_cast; +mod wrong_number_of_generic_args; + +pub use self::{ + missing_cast_for_variadic_arg::*, sized_unsized_cast::*, wrong_number_of_generic_args::*, +}; + +use rustc_errors::{DiagnosticBuilder, DiagnosticId}; use rustc_session::Session; -use rustc_span::Span; pub trait StructuredDiagnostic<'tcx> { fn session(&self) -> &Session; fn code(&self) -> DiagnosticId; - fn common(&self) -> DiagnosticBuilder<'tcx>; - fn diagnostic(&self) -> DiagnosticBuilder<'tcx> { - let err = self.common(); - if self.session().teach(&self.code()) { self.extended(err) } else { self.regular(err) } - } - - fn regular(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { - err - } - - fn extended(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { - err - } -} - -pub struct VariadicError<'tcx> { - sess: &'tcx Session, - span: Span, - t: Ty<'tcx>, - cast_ty: &'tcx str, -} - -impl<'tcx> VariadicError<'tcx> { - pub fn new( - sess: &'tcx Session, - span: Span, - t: Ty<'tcx>, - cast_ty: &'tcx str, - ) -> VariadicError<'tcx> { - VariadicError { sess, span, t, cast_ty } - } -} - -impl<'tcx> StructuredDiagnostic<'tcx> for VariadicError<'tcx> { - fn session(&self) -> &Session { - self.sess - } - - fn code(&self) -> DiagnosticId { - rustc_errors::error_code!(E0617) - } + let err = self.diagnostic_common(); - fn common(&self) -> DiagnosticBuilder<'tcx> { - let mut err = if self.t.references_error() { - self.sess.diagnostic().struct_dummy() + if self.session().teach(&self.code()) { + self.diagnostic_extended(err) } else { - self.sess.struct_span_fatal_with_code( - self.span, - &format!("can't pass `{}` to variadic function", self.t), - self.code(), - ) - }; - if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.span) { - err.span_suggestion( - self.span, - &format!("cast the value to `{}`", self.cast_ty), - format!("{} as {}", snippet, self.cast_ty), - Applicability::MachineApplicable, - ); - } else { - err.help(&format!("cast the value to `{}`", self.cast_ty)); + self.diagnostic_regular(err) } - err - } - - fn extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { - err.note(&format!( - "certain types, like `{}`, must be cast before passing them to a \ - variadic function, because of arcane ABI rules dictated by the C \ - standard", - self.t - )); - err - } -} - -pub struct SizedUnsizedCastError<'tcx> { - sess: &'tcx Session, - span: Span, - expr_ty: Ty<'tcx>, - cast_ty: String, -} - -impl<'tcx> SizedUnsizedCastError<'tcx> { - pub fn new( - sess: &'tcx Session, - span: Span, - expr_ty: Ty<'tcx>, - cast_ty: String, - ) -> SizedUnsizedCastError<'tcx> { - SizedUnsizedCastError { sess, span, expr_ty, cast_ty } } -} -impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCastError<'tcx> { - fn session(&self) -> &Session { - self.sess - } + fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx>; - fn code(&self) -> DiagnosticId { - rustc_errors::error_code!(E0607) - } - - fn common(&self) -> DiagnosticBuilder<'tcx> { - if self.expr_ty.references_error() { - self.sess.diagnostic().struct_dummy() - } else { - self.sess.struct_span_fatal_with_code( - self.span, - &format!( - "cannot cast thin pointer `{}` to fat pointer `{}`", - self.expr_ty, self.cast_ty - ), - self.code(), - ) - } + fn diagnostic_regular(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { + err } - fn extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { - err.help( - "Thin pointers are \"simple\" pointers: they are purely a reference to a -memory address. - -Fat pointers are pointers referencing \"Dynamically Sized Types\" (also -called DST). DST don't have a statically known size, therefore they can -only exist behind some kind of pointers that contain additional -information. Slices and trait objects are DSTs. In the case of slices, -the additional information the fat pointer holds is their size. - -To fix this error, don't try to cast directly between thin and fat -pointers. - -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions", - ); + fn diagnostic_extended(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { err } } diff --git a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs new file mode 100644 index 0000000000..674b0e463f --- /dev/null +++ b/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs @@ -0,0 +1,58 @@ +use crate::structured_errors::StructuredDiagnostic; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_middle::ty::{Ty, TypeFoldable}; +use rustc_session::Session; +use rustc_span::Span; + +pub struct MissingCastForVariadicArg<'tcx> { + pub sess: &'tcx Session, + pub span: Span, + pub ty: Ty<'tcx>, + pub cast_ty: &'tcx str, +} + +impl<'tcx> StructuredDiagnostic<'tcx> for MissingCastForVariadicArg<'tcx> { + fn session(&self) -> &Session { + self.sess + } + + fn code(&self) -> DiagnosticId { + rustc_errors::error_code!(E0617) + } + + fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> { + let mut err = if self.ty.references_error() { + self.sess.diagnostic().struct_dummy() + } else { + self.sess.struct_span_fatal_with_code( + self.span, + &format!("can't pass `{}` to variadic function", self.ty), + self.code(), + ) + }; + + if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.span) { + err.span_suggestion( + self.span, + &format!("cast the value to `{}`", self.cast_ty), + format!("{} as {}", snippet, self.cast_ty), + Applicability::MachineApplicable, + ); + } else { + err.help(&format!("cast the value to `{}`", self.cast_ty)); + } + + err + } + + fn diagnostic_extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { + err.note(&format!( + "certain types, like `{}`, must be casted before passing them to a \ + variadic function, because of arcane ABI rules dictated by the C \ + standard", + self.ty + )); + + err + } +} diff --git a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs new file mode 100644 index 0000000000..d0477a3e74 --- /dev/null +++ b/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs @@ -0,0 +1,57 @@ +use crate::structured_errors::StructuredDiagnostic; +use rustc_errors::{DiagnosticBuilder, DiagnosticId}; +use rustc_middle::ty::{Ty, TypeFoldable}; +use rustc_session::Session; +use rustc_span::Span; + +pub struct SizedUnsizedCast<'tcx> { + pub sess: &'tcx Session, + pub span: Span, + pub expr_ty: Ty<'tcx>, + pub cast_ty: String, +} + +impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCast<'tcx> { + fn session(&self) -> &Session { + self.sess + } + + fn code(&self) -> DiagnosticId { + rustc_errors::error_code!(E0607) + } + + fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> { + if self.expr_ty.references_error() { + self.sess.diagnostic().struct_dummy() + } else { + self.sess.struct_span_fatal_with_code( + self.span, + &format!( + "cannot cast thin pointer `{}` to fat pointer `{}`", + self.expr_ty, self.cast_ty + ), + self.code(), + ) + } + } + + fn diagnostic_extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> { + err.help( + "Thin pointers are \"simple\" pointers: they are purely a reference to a +memory address. + +Fat pointers are pointers referencing \"Dynamically Sized Types\" (also +called DST). DST don't have a statically known size, therefore they can +only exist behind some kind of pointers that contain additional +information. Slices and trait objects are DSTs. In the case of slices, +the additional information the fat pointer holds is their size. + +To fix this error, don't try to cast directly between thin and fat +pointers. + +For more information about casts, take a look at The Book: +https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions", + ); + err + } +} diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs new file mode 100644 index 0000000000..e35c155746 --- /dev/null +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs @@ -0,0 +1,393 @@ +use crate::structured_errors::StructuredDiagnostic; +use hir::def::DefKind; +use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_hir as hir; +use rustc_middle::ty::{self as ty, TyCtxt}; +use rustc_session::Session; +use rustc_span::Span; +use rustc_span::{def_id::DefId, MultiSpan}; + +/// Handles the `wrong number of type / lifetime / ... arguments` family of error messages. +pub struct WrongNumberOfGenericArgs<'a, 'tcx> { + crate tcx: TyCtxt<'tcx>, + + /// "type", "lifetime" etc., put verbatim into the message + crate kind: &'static str, + + /// Minimum number of expected generic arguments (e.g. `2` for `HashMap`) + crate expected_min: usize, + + /// Maximum number of expected generic arguments (e.g. `3` for `HashMap`) + crate expected_max: usize, + + /// Number of generic arguments provided by the user + crate provided: usize, + + /// Offset into `gen_params` - depends on the `kind`; might be different than `args_offset` when + /// user passed e.g. more arguments than was actually expected + crate params_offset: usize, + + /// Offset into `gen_args` - depends on the `kind` + crate args_offset: usize, + + /// Offending path segment + crate path_segment: &'a hir::PathSegment<'a>, + + /// Generic parameters as expected by type or trait + crate gen_params: &'a ty::Generics, + + /// Generic arguments as provided by user + crate gen_args: &'a hir::GenericArgs<'a>, + + /// DefId of the generic type + crate def_id: DefId, + + /// Offending place where the generic type has been misused + crate span: Span, +} + +impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> { + fn quantifier_and_bound(&self) -> (&'static str, usize) { + if self.expected_min == self.expected_max { + ("", self.expected_min) + } else if self.provided < self.expected_min { + ("at least ", self.expected_min) + } else { + ("at most ", self.expected_max) + } + } + + fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx> { + let span = self.path_segment.ident.span; + + let msg = { + let def_path = self.tcx.def_path_str(self.def_id); + let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id); + let (quantifier, bound) = self.quantifier_and_bound(); + + if self.gen_args.span().is_some() { + format!( + "this {} takes {}{} {} argument{} but {}{} {} argument{} {} supplied", + def_kind, + quantifier, + bound, + self.kind, + pluralize!(bound), + if self.provided > 0 && self.provided < self.expected_min { + "only " + } else { + "" + }, + self.provided, + self.kind, + pluralize!(self.provided), + if self.provided == 1 { "was" } else { "were" }, + ) + } else { + format!("missing generics for {} `{}`", def_kind, def_path) + } + }; + + self.tcx.sess.struct_span_err_with_code(span, &msg, self.code()) + } + + /// Builds the `expected 1 type argument / supplied 2 type arguments` message. + fn notify(&self, err: &mut DiagnosticBuilder<'_>) { + let (quantifier, bound) = self.quantifier_and_bound(); + + err.span_label( + self.path_segment.ident.span, + format!( + "expected {}{} {} argument{}", + quantifier, + bound, + self.kind, + pluralize!(bound), + ), + ); + + // When user's provided too many arguments, we don't highlight each of them, because it + // would overlap with the suggestion to remove them: + // + // ``` + // type Foo = Bar; + // ----- ----- supplied 2 type arguments + // ^^^^^^^ remove this type argument + // ``` + if self.provided > self.expected_max { + return; + } + + let args = self.gen_args.args.iter().skip(self.args_offset).take(self.provided).enumerate(); + + for (i, arg) in args { + err.span_label( + arg.span(), + if i + 1 == self.provided { + format!( + "supplied {} {} argument{}", + self.provided, + self.kind, + pluralize!(self.provided) + ) + } else { + String::new() + }, + ); + } + } + + fn suggest(&self, err: &mut DiagnosticBuilder<'_>) { + if self.provided == 0 { + if self.gen_args.span().is_some() { + self.suggest_adding_args(err); + } else { + self.suggest_creating_generics(err); + } + } else if self.provided < self.expected_min { + self.suggest_adding_args(err); + } else { + self.suggest_removing_args_or_generics(err); + } + } + + /// Suggests to create generics (`<...>`) when current invocation site contains no generics at + /// all: + /// + /// ```text + /// type Map = HashMap; + /// ``` + fn suggest_creating_generics(&self, err: &mut DiagnosticBuilder<'_>) { + let params = self + .gen_params + .params + .iter() + .skip(self.params_offset) + .take(self.expected_min) + .map(|param| param.name.to_string()) + .collect::>() + .join(", "); + + let def_kind = self.tcx.def_kind(self.def_id); + + let sugg = if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) { + format!("::<{}>", params) + } else { + format!("<{}>", params) + }; + + let msg = format!( + "use angle brackets to add missing {} argument{}", + self.kind, + pluralize!(self.expected_min), + ); + + err.span_suggestion_verbose( + self.path_segment.ident.span.shrink_to_hi(), + &msg, + sugg, + Applicability::HasPlaceholders, + ); + } + + /// Suggests to add missing argument(s) when current invocation site already contains some + /// generics: + /// + /// ```text + /// type Map = HashMap; + /// ``` + fn suggest_adding_args(&self, err: &mut DiagnosticBuilder<'_>) { + assert!(!self.gen_args.is_empty()); + + if self.gen_args.parenthesized { + return; + } + + let missing_arg_count = self.expected_min - self.provided; + + let (span, sugg_prefix) = if self.args_offset + self.provided == 0 { + let span = self.gen_args.args[0].span().shrink_to_lo(); + (span, "") + } else { + let span = + self.gen_args.args[self.args_offset + self.provided - 1].span().shrink_to_hi(); + (span, ", ") + }; + + let msg = format!("add missing {} argument{}", self.kind, pluralize!(missing_arg_count)); + + let sugg = self + .gen_params + .params + .iter() + .skip(self.params_offset + self.provided) + .take(missing_arg_count) + .map(|param| param.name.to_string()) + .collect::>() + .join(", "); + + let sugg = format!("{}{}", sugg_prefix, sugg); + + err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders); + } + + /// Suggests to remove redundant argument(s): + /// + /// ```text + /// type Map = HashMap; + /// ``` + fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) { + assert!(self.provided > 0); + + let redundant_args_count = self.provided - self.expected_max; + let remove_entire_generics = redundant_args_count >= self.gen_args.args.len(); + + let (span, msg) = if remove_entire_generics { + let sm = self.tcx.sess.source_map(); + + let span = self + .path_segment + .args + .unwrap() + .span_ext(sm) + .unwrap() + .with_lo(self.path_segment.ident.span.hi()); + + let msg = format!( + "remove these {}generics", + if self.gen_args.parenthesized { "parenthetical " } else { "" }, + ); + + (span, msg) + } else { + // When it comes to removing particular argument(s) from the generics, there are two + // edge cases we have to consider: + // + // When the first redundant argument is at the beginning or in the middle of the + // generics, like so: + // + // ``` + // type Map = HashMap; + // ^^^^^^^^^^^^^^^^ + // | span must start with the argument + // ``` + // + // When the last redundant argument is at the ending of the generics, like so: + // + // ``` + // type Map = HashMap; + // ^^^^^^^^^^^^^^^^ + // | span must start with the comma + // ``` + + // Index of the first redundant argument + let from_idx = self.args_offset + self.expected_max; + + // Index of the last redundant argument + let to_idx = self.args_offset + self.provided - 1; + + assert!(from_idx <= to_idx); + + let (from, comma_eaten) = { + let first_argument_starts_generics = from_idx == 0; + let last_argument_ends_generics = to_idx + 1 == self.gen_args.args.len(); + + if !first_argument_starts_generics && last_argument_ends_generics { + (self.gen_args.args[from_idx - 1].span().hi(), true) + } else { + (self.gen_args.args[from_idx].span().lo(), false) + } + }; + + let to = { + let hi = self.gen_args.args[to_idx].span().hi(); + + if comma_eaten { + hi + } else { + self.gen_args.args.get(to_idx + 1).map(|arg| arg.span().lo()).unwrap_or(hi) + } + }; + + let span = Span::new(from, to, self.span.ctxt()); + + let msg = format!( + "remove {} {} argument{}", + if redundant_args_count == 1 { "this" } else { "these" }, + self.kind, + pluralize!(redundant_args_count), + ); + + (span, msg) + }; + + err.span_suggestion(span, &msg, String::new(), Applicability::MaybeIncorrect); + } + + /// 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() + } else { + return; + }; + + let msg = { + let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id); + let (quantifier, bound) = self.quantifier_and_bound(); + + let params = if bound == 0 { + String::new() + } else { + let params = self + .gen_params + .params + .iter() + .skip(self.params_offset) + .take(bound) + .map(|param| { + let span = self.tcx.def_span(param.def_id); + spans.push_span_label(span, String::new()); + param + }) + .map(|param| format!("`{}`", param.name)) + .collect::>() + .join(", "); + + format!(": {}", params) + }; + + format!( + "{} defined here, with {}{} {} parameter{}{}", + def_kind, + quantifier, + bound, + self.kind, + pluralize!(bound), + params, + ) + }; + + err.span_note(spans, &msg); + } +} + +impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> { + fn session(&self) -> &Session { + self.tcx.sess + } + + fn code(&self) -> DiagnosticId { + rustc_errors::error_code!(E0107) + } + + fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> { + let mut err = self.start_diagnostics(); + + self.notify(&mut err); + self.suggest(&mut err); + self.show_definition(&mut err); + + err + } +} diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs index a8fbdfb7c6..339eb5f9af 100644 --- a/compiler/rustc_typeck/src/variance/constraints.rs +++ b/compiler/rustc_typeck/src/variance/constraints.rs @@ -207,27 +207,13 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } } - fn add_constraints_from_trait_ref( - &mut self, - current: &CurrentItem, - trait_ref: ty::TraitRef<'tcx>, - variance: VarianceTermPtr<'a>, - ) { - debug!("add_constraints_from_trait_ref: trait_ref={:?} variance={:?}", trait_ref, variance); - self.add_constraints_from_invariant_substs(current, trait_ref.substs, variance); - } - + #[instrument(skip(self, current))] fn add_constraints_from_invariant_substs( &mut self, current: &CurrentItem, substs: SubstsRef<'tcx>, variance: VarianceTermPtr<'a>, ) { - debug!( - "add_constraints_from_invariant_substs: substs={:?} variance={:?}", - substs, variance - ); - // Trait are always invariant so we can take advantage of that. let variance_i = self.invariant(variance); @@ -300,8 +286,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::Projection(ref data) => { - let tcx = self.tcx(); - self.add_constraints_from_trait_ref(current, data.trait_ref(tcx), variance); + self.add_constraints_from_invariant_substs(current, data.substs, variance); } ty::Opaque(_, substs) => { diff --git a/config.toml.example b/config.toml.example index b1fb8904ca..55b20adabd 100644 --- a/config.toml.example +++ b/config.toml.example @@ -35,9 +35,11 @@ changelog-seen = 2 # Unless you're developing for a target where Rust CI doesn't build a compiler # toolchain or changing LLVM locally, you probably want to set this to true. # -# It's currently false by default due to being newly added; please file bugs if -# enabling this did not work for you on x86_64-unknown-linux-gnu. -# Other target triples are currently not supported; see #77084. +# This is false by default so that distributions don't unexpectedly download +# LLVM from the internet. +# +# All tier 1 targets are currently supported; set this to `"if-supported"` if +# you are not sure whether you're on a tier 1 target. # # We also currently only support this when building LLVM for the build triple. # @@ -669,3 +671,7 @@ changelog-seen = 2 # Whether to allow failures when building tools #missing-tools = false + +# List of compression formats to use when generating dist tarballs. The list of +# formats is provided to rust-installer, which must support all of them. +#compression-formats = ["gz", "xz"] diff --git a/git-commit-hash b/git-commit-hash index cb573231ff..b5845acd91 100644 --- a/git-commit-hash +++ b/git-commit-hash @@ -1 +1 @@ -cb75ad5db02783e8b0222fee363c5f63f7e2cf5b \ No newline at end of file +2fd73fabe469357a12c2c974c140f67e7cdd76d0 \ No newline at end of file diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index eff197d998..d95b5b7f17 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.10", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.39", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = "0.7" @@ -31,5 +31,5 @@ harness = false [features] compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] -compiler-builtins-asm = ["compiler_builtins/asm"] +compiler-builtins-no-asm = ["compiler_builtins/no-asm"] compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"] diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 4fbcc4590f..cb9daaea00 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -397,3 +397,26 @@ pub mod __alloc_error_handler { unsafe { oom_impl(layout) } } } + +/// Specialize clones into pre-allocated, uninitialized memory. +/// Used by `Box::clone` and `Rc`/`Arc::make_mut`. +pub(crate) trait WriteCloneIntoRaw: Sized { + unsafe fn write_clone_into_raw(&self, target: *mut Self); +} + +impl WriteCloneIntoRaw for T { + #[inline] + default unsafe fn write_clone_into_raw(&self, target: *mut Self) { + // Having allocated *first* may allow the optimizer to create + // the cloned value in-place, skipping the local and move. + unsafe { target.write(self.clone()) }; + } +} + +impl WriteCloneIntoRaw for T { + #[inline] + unsafe fn write_clone_into_raw(&self, target: *mut Self) { + // We can always copy in-place, without ever involving a local value. + unsafe { target.copy_from_nonoverlapping(self, 1) }; + } +} diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index f801c1ac75..adf996fc78 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -103,6 +103,11 @@ where /// is desired, `to_mut` will obtain a mutable reference to an owned /// value, cloning if necessary. /// +/// If you need reference-counting pointers, note that +/// [`Rc::make_mut`][crate::rc::Rc::make_mut] and +/// [`Arc::make_mut`][crate::sync::Arc::make_mut] can provide clone-on-write +/// functionality as well. +/// /// # Examples /// /// ``` diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index a6360f25ec..949079e5b6 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -126,9 +126,7 @@ //! //! [ucg#198]: https://github.com/rust-lang/unsafe-code-guidelines/issues/198 //! [dereferencing]: core::ops::Deref -//! [`Box`]: Box //! [`Box::::from_raw(value)`]: Box::from_raw -//! [`Box::::into_raw`]: Box::into_raw //! [`Global`]: crate::alloc::Global //! [`Layout`]: crate::alloc::Layout //! [`Layout::for_value(&*value)`]: crate::alloc::Layout::for_value @@ -151,9 +149,10 @@ use core::ops::{ }; use core::pin::Pin; use core::ptr::{self, Unique}; +use core::stream::Stream; use core::task::{Context, Poll}; -use crate::alloc::{handle_alloc_error, Allocator, Global, Layout}; +use crate::alloc::{handle_alloc_error, AllocError, Allocator, Global, Layout, WriteCloneIntoRaw}; use crate::borrow::Cow; use crate::raw_vec::RawVec; use crate::str::from_boxed_utf8_unchecked; @@ -180,8 +179,10 @@ impl Box { /// ``` /// let five = Box::new(5); /// ``` - #[stable(feature = "rust1", since = "1.0.0")] #[inline(always)] + #[doc(alias = "alloc")] + #[doc(alias = "malloc")] + #[stable(feature = "rust1", since = "1.0.0")] pub fn new(x: T) -> Self { box x } @@ -228,8 +229,9 @@ impl Box { /// ``` /// /// [zeroed]: mem::MaybeUninit::zeroed - #[unstable(feature = "new_uninit", issue = "63291")] #[inline] + #[doc(alias = "calloc")] + #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_zeroed() -> Box> { Self::new_zeroed_in(Global) } @@ -241,6 +243,78 @@ impl Box { pub fn pin(x: T) -> Pin> { (box x).into() } + + /// Allocates memory on the heap then places `x` into it, + /// returning an error if the allocation fails + /// + /// This doesn't actually allocate if `T` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// + /// let five = Box::try_new(5)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn try_new(x: T) -> Result { + Self::try_new_in(x, Global) + } + + /// Constructs a new box with uninitialized contents on the heap, + /// returning an error if the allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// let mut five = Box::::try_new_uninit()?; + /// + /// let five = unsafe { + /// // Deferred initialization: + /// five.as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + #[inline] + pub fn try_new_uninit() -> Result>, AllocError> { + Box::try_new_uninit_in(Global) + } + + /// Constructs a new `Box` with uninitialized contents, with the memory + /// being filled with `0` bytes on the heap + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// let zero = Box::::try_new_zeroed()?; + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + #[inline] + pub fn try_new_zeroed() -> Result>, AllocError> { + Box::try_new_zeroed_in(Global) + } } impl Box { @@ -267,6 +341,31 @@ impl Box { } } + /// Allocates memory in the given allocator then places `x` into it, + /// returning an error if the allocation fails + /// + /// This doesn't actually allocate if `T` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// + /// let five = Box::try_new_in(5, System)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn try_new_in(x: T, alloc: A) -> Result { + let mut boxed = Self::try_new_uninit_in(alloc)?; + unsafe { + boxed.as_mut_ptr().write(x); + Ok(boxed.assume_init()) + } + } + /// Constructs a new box with uninitialized contents in the provided allocator. /// /// # Examples @@ -291,8 +390,37 @@ impl Box { // #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_uninit_in(alloc: A) -> Box, A> { let layout = Layout::new::>(); - let ptr = alloc.allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)).cast(); - unsafe { Box::from_raw_in(ptr.as_ptr(), alloc) } + Box::try_new_uninit_in(alloc).unwrap_or_else(|_| handle_alloc_error(layout)) + } + + /// Constructs a new box with uninitialized contents in the provided allocator, + /// returning an error if the allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::alloc::System; + /// + /// let mut five = Box::::try_new_uninit_in(System)?; + /// + /// let five = unsafe { + /// // Deferred initialization: + /// five.as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { + let layout = Layout::new::>(); + let ptr = alloc.allocate(layout)?.cast(); + unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) } } /// Constructs a new `Box` with uninitialized contents, with the memory @@ -319,9 +447,37 @@ impl Box { // #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_zeroed_in(alloc: A) -> Box, A> { let layout = Layout::new::>(); - let ptr = - alloc.allocate_zeroed(layout).unwrap_or_else(|_| handle_alloc_error(layout)).cast(); - unsafe { Box::from_raw_in(ptr.as_ptr(), alloc) } + Box::try_new_zeroed_in(alloc).unwrap_or_else(|_| handle_alloc_error(layout)) + } + + /// Constructs a new `Box` with uninitialized contents, with the memory + /// being filled with `0` bytes in the provided allocator, + /// returning an error if the allocation fails, + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::alloc::System; + /// + /// let zero = Box::::try_new_zeroed_in(System)?; + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { + let layout = Layout::new::>(); + let ptr = alloc.allocate_zeroed(layout)?.cast(); + unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) } } /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then @@ -862,10 +1018,14 @@ impl Clone for Box { /// // But they are unique objects /// assert_ne!(&*x as *const i32, &*y as *const i32); /// ``` - #[rustfmt::skip] #[inline] fn clone(&self) -> Self { - Self::new_in((**self).clone(), self.1.clone()) + // Pre-allocate memory to allow writing the cloned value directly. + let mut boxed = Self::new_uninit_in(self.1.clone()); + unsafe { + (**self).write_clone_into_raw(boxed.as_mut_ptr()); + boxed.assume_init() + } } /// Copies `source`'s contents into `self` without creating a new allocation. @@ -1212,6 +1372,39 @@ impl Box { } } +impl Box { + #[inline] + #[stable(feature = "box_send_sync_any_downcast", since = "1.51.0")] + /// Attempt to downcast the box to a concrete type. + /// + /// # Examples + /// + /// ``` + /// use std::any::Any; + /// + /// fn print_if_string(value: Box) { + /// if let Ok(string) = value.downcast::() { + /// println!("String ({}): {}", string.len(), string); + /// } + /// } + /// + /// let my_string = "Hello World".to_string(); + /// print_if_string(Box::new(my_string)); + /// print_if_string(Box::new(0i8)); + /// ``` + pub fn downcast(self) -> Result, Self> { + if self.is::() { + unsafe { + let (raw, alloc): (*mut (dyn Any + Send + Sync), _) = + Box::into_raw_with_allocator(self); + Ok(Box::from_raw_in(raw as *mut T, alloc)) + } + } else { + Err(self) + } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Box { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1462,3 +1655,16 @@ where F::poll(Pin::new(&mut *self), cx) } } + +#[unstable(feature = "async_stream", issue = "79024")] +impl Stream for Box { + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut **self).poll_next(cx) + } + + fn size_hint(&self) -> (usize, Option) { + (**self).size_hint() + } +} diff --git a/library/alloc/src/collections/binary_heap.rs b/library/alloc/src/collections/binary_heap.rs index 97ebc12175..8a36b2af76 100644 --- a/library/alloc/src/collections/binary_heap.rs +++ b/library/alloc/src/collections/binary_heap.rs @@ -161,7 +161,10 @@ use super::SpecExtend; /// It is a logic error for an item to be modified in such a way that the /// item's ordering relative to any other item, as determined by the `Ord` /// trait, changes while it is in the heap. This is normally only possible -/// through `Cell`, `RefCell`, global state, I/O, or unsafe code. +/// through `Cell`, `RefCell`, global state, I/O, or unsafe code. The +/// behavior resulting from such a logic error is not specified, but will +/// not result in undefined behavior. This could include panics, incorrect +/// results, aborts, memory leaks, and non-termination. /// /// # Examples /// @@ -630,10 +633,16 @@ impl BinaryHeap { // and about 2 * (len1 + len2) comparisons in the worst case // while `extend` takes O(len2 * log(len1)) operations // and about 1 * len2 * log_2(len1) comparisons in the worst case, - // assuming len1 >= len2. + // assuming len1 >= len2. For larger heaps, the crossover point + // no longer follows this reasoning and was determined empirically. #[inline] fn better_to_rebuild(len1: usize, len2: usize) -> bool { - 2 * (len1 + len2) < len2 * log2_fast(len1) + let tot_len = len1 + len2; + if tot_len <= 2048 { + 2 * tot_len < len2 * log2_fast(len1) + } else { + 2 * tot_len < len2 * 11 + } } if better_to_rebuild(self.len(), other.len()) { @@ -861,8 +870,7 @@ impl BinaryHeap { /// The capacity will remain at least as large as both the length /// and the supplied value. /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. + /// If the current capacity is less than the lower limit, this is a no-op. /// /// # Examples /// @@ -915,6 +923,7 @@ impl BinaryHeap { /// /// assert_eq!(heap.len(), 2); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { self.data.len() diff --git a/library/alloc/src/collections/btree/append.rs b/library/alloc/src/collections/btree/append.rs index bd99c4ed2f..1d6488dd2d 100644 --- a/library/alloc/src/collections/btree/append.rs +++ b/library/alloc/src/collections/btree/append.rs @@ -30,7 +30,7 @@ impl Root { /// Pushes all key-value pairs to the end of the tree, incrementing a /// `length` variable along the way. The latter makes it easier for the /// caller to avoid a leak when the iterator panicks. - fn bulk_push(&mut self, iter: I, length: &mut usize) + pub fn bulk_push(&mut self, iter: I, length: &mut usize) where I: Iterator, { @@ -81,15 +81,18 @@ impl Root { // the appended elements even if advancing the iterator panicks. *length += 1; } - self.fix_right_edge(); + self.fix_right_border_of_plentiful(); } - fn fix_right_edge(&mut self) { - // Handle underfull nodes, start from the top. + /// Stock up any underfull nodes on the right border of the tree. + /// The other nodes, those that are not the root nor a rightmost edge, + /// must have MIN_LEN elements to spare. + fn fix_right_border_of_plentiful(&mut self) { let mut cur_node = self.borrow_mut(); while let Internal(internal) = cur_node.force() { // Check if right-most child is underfull. let mut last_kv = internal.last_kv().consider_for_balancing(); + debug_assert!(last_kv.left_child_len() >= MIN_LEN * 2); let right_child_len = last_kv.right_child_len(); if right_child_len < MIN_LEN { // We need to steal. diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 735213363f..946267d17c 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -10,7 +10,7 @@ use core::ptr; use super::borrow::DormantMutRef; use super::node::{self, marker, ForceResult::*, Handle, NodeRef, Root}; -use super::search::{self, SearchResult::*}; +use super::search::SearchResult::*; use super::unwrap_unchecked; mod entry; @@ -51,6 +51,9 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// It is a logic error for a key to be modified in such a way that the key's ordering relative to /// any other key, as determined by the [`Ord`] trait, changes while it is in the map. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. +/// The behavior resulting from such a logic error is not specified, but will not result in +/// undefined behavior. This could include panics, incorrect results, aborts, memory leaks, and +/// non-termination. /// /// [`Cell`]: core::cell::Cell /// [`RefCell`]: core::cell::RefCell @@ -227,7 +230,7 @@ where fn get(&self, key: &Q) -> Option<&K> { let root_node = self.root.as_ref()?.reborrow(); - match search::search_tree(root_node, key) { + match root_node.search_tree(key) { Found(handle) => Some(handle.into_kv().0), GoDown(_) => None, } @@ -236,7 +239,7 @@ where fn take(&mut self, key: &Q) -> Option { let (map, dormant_map) = DormantMutRef::new(self); let root_node = map.root.as_mut()?.borrow_mut(); - match search::search_tree(root_node, key) { + match root_node.search_tree(key) { Found(handle) => { Some(OccupiedEntry { handle, dormant_map, _marker: PhantomData }.remove_kv().0) } @@ -247,7 +250,7 @@ where fn replace(&mut self, key: K) -> Option { let (map, dormant_map) = DormantMutRef::new(self); let root_node = Self::ensure_is_owned(&mut map.root).borrow_mut(); - match search::search_tree::, K, (), K>(root_node, &key) { + match root_node.search_tree::(&key) { Found(mut kv) => Some(mem::replace(kv.key_mut(), key)), GoDown(handle) => { VacantEntry { key, handle, dormant_map, _marker: PhantomData }.insert(()); @@ -297,8 +300,8 @@ pub struct IterMut<'a, K: 'a, V: 'a> { /// [`into_iter`]: IntoIterator::into_iter #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter { - front: Option, marker::Edge>>, - back: Option, marker::Edge>>, + front: Option, marker::Edge>>, + back: Option, marker::Edge>>, length: usize, } @@ -457,7 +460,7 @@ impl fmt::Debug for RangeMut<'_, K, V> { } } -impl BTreeMap { +impl BTreeMap { /// Makes a new, empty `BTreeMap`. /// /// Does not allocate anything on its own. @@ -476,7 +479,10 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] - pub const fn new() -> BTreeMap { + pub const fn new() -> BTreeMap + where + K: Ord, + { BTreeMap { root: None, length: 0 } } @@ -495,7 +501,10 @@ impl BTreeMap { /// assert!(a.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { + pub fn clear(&mut self) + where + K: Ord, + { *self = BTreeMap::new(); } @@ -519,11 +528,11 @@ impl BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] pub fn get(&self, key: &Q) -> Option<&V> where - K: Borrow, + K: Borrow + Ord, Q: Ord, { let root_node = self.root.as_ref()?.reborrow(); - match search::search_tree(root_node, key) { + match root_node.search_tree(key) { Found(handle) => Some(handle.into_kv().1), GoDown(_) => None, } @@ -547,11 +556,11 @@ impl BTreeMap { #[stable(feature = "map_get_key_value", since = "1.40.0")] pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> where - K: Borrow, + K: Borrow + Ord, Q: Ord, { let root_node = self.root.as_ref()?.reborrow(); - match search::search_tree(root_node, k) { + match root_node.search_tree(k) { Found(handle) => Some(handle.into_kv()), GoDown(_) => None, } @@ -575,7 +584,10 @@ impl BTreeMap { /// assert_eq!(map.first_key_value(), Some((&1, &"b"))); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn first_key_value(&self) -> Option<(&K, &V)> { + pub fn first_key_value(&self) -> Option<(&K, &V)> + where + K: Ord, + { let root_node = self.root.as_ref()?.reborrow(); root_node.first_leaf_edge().right_kv().ok().map(Handle::into_kv) } @@ -601,7 +613,10 @@ impl BTreeMap { /// assert_eq!(*map.get(&2).unwrap(), "b"); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn first_entry(&mut self) -> Option> { + pub fn first_entry(&mut self) -> Option> + where + K: Ord, + { let (map, dormant_map) = DormantMutRef::new(self); let root_node = map.root.as_mut()?.borrow_mut(); let kv = root_node.first_leaf_edge().right_kv().ok()?; @@ -628,7 +643,10 @@ impl BTreeMap { /// assert!(map.is_empty()); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn pop_first(&mut self) -> Option<(K, V)> { + pub fn pop_first(&mut self) -> Option<(K, V)> + where + K: Ord, + { self.first_entry().map(|entry| entry.remove_entry()) } @@ -649,7 +667,10 @@ impl BTreeMap { /// assert_eq!(map.last_key_value(), Some((&2, &"a"))); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn last_key_value(&self) -> Option<(&K, &V)> { + pub fn last_key_value(&self) -> Option<(&K, &V)> + where + K: Ord, + { let root_node = self.root.as_ref()?.reborrow(); root_node.last_leaf_edge().left_kv().ok().map(Handle::into_kv) } @@ -675,7 +696,10 @@ impl BTreeMap { /// assert_eq!(*map.get(&2).unwrap(), "last"); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn last_entry(&mut self) -> Option> { + pub fn last_entry(&mut self) -> Option> + where + K: Ord, + { let (map, dormant_map) = DormantMutRef::new(self); let root_node = map.root.as_mut()?.borrow_mut(); let kv = root_node.last_leaf_edge().left_kv().ok()?; @@ -702,7 +726,10 @@ impl BTreeMap { /// assert!(map.is_empty()); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn pop_last(&mut self) -> Option<(K, V)> { + pub fn pop_last(&mut self) -> Option<(K, V)> + where + K: Ord, + { self.last_entry().map(|entry| entry.remove_entry()) } @@ -726,7 +753,7 @@ impl BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] pub fn contains_key(&self, key: &Q) -> bool where - K: Borrow, + K: Borrow + Ord, Q: Ord, { self.get(key).is_some() @@ -755,11 +782,11 @@ impl BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> where - K: Borrow, + K: Borrow + Ord, Q: Ord, { let root_node = self.root.as_mut()?.borrow_mut(); - match search::search_tree(root_node, key) { + match root_node.search_tree(key) { Found(handle) => Some(handle.into_val_mut()), GoDown(_) => None, } @@ -792,7 +819,10 @@ impl BTreeMap { /// assert_eq!(map[&37], "c"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, key: K, value: V) -> Option { + pub fn insert(&mut self, key: K, value: V) -> Option + where + K: Ord, + { match self.entry(key) { Occupied(mut entry) => Some(entry.insert(value)), Vacant(entry) => { @@ -820,10 +850,11 @@ impl BTreeMap { /// assert_eq!(map.remove(&1), Some("a")); /// assert_eq!(map.remove(&1), None); /// ``` + #[doc(alias = "delete")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(&mut self, key: &Q) -> Option where - K: Borrow, + K: Borrow + Ord, Q: Ord, { self.remove_entry(key).map(|(_, v)| v) @@ -850,12 +881,12 @@ impl BTreeMap { #[stable(feature = "btreemap_remove_entry", since = "1.45.0")] pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> where - K: Borrow, + K: Borrow + Ord, Q: Ord, { let (map, dormant_map) = DormantMutRef::new(self); let root_node = map.root.as_mut()?.borrow_mut(); - match search::search_tree(root_node, key) { + match root_node.search_tree(key) { Found(handle) => { Some(OccupiedEntry { handle, dormant_map, _marker: PhantomData }.remove_entry()) } @@ -882,6 +913,7 @@ impl BTreeMap { #[unstable(feature = "btree_retain", issue = "79025")] pub fn retain(&mut self, mut f: F) where + K: Ord, F: FnMut(&K, &mut V) -> bool, { self.drain_filter(|k, v| !f(k, v)); @@ -916,7 +948,10 @@ impl BTreeMap { /// assert_eq!(a[&5], "f"); /// ``` #[stable(feature = "btree_append", since = "1.11.0")] - pub fn append(&mut self, other: &mut Self) { + pub fn append(&mut self, other: &mut Self) + where + K: Ord, + { // Do we have to append anything at all? if other.is_empty() { return; @@ -967,7 +1002,7 @@ impl BTreeMap { pub fn range(&self, range: R) -> Range<'_, K, V> where T: Ord, - K: Borrow, + K: Borrow + Ord, R: RangeBounds, { if let Some(root) = &self.root { @@ -1013,7 +1048,7 @@ impl BTreeMap { pub fn range_mut(&mut self, range: R) -> RangeMut<'_, K, V> where T: Ord, - K: Borrow, + K: Borrow + Ord, R: RangeBounds, { if let Some(root) = &mut self.root { @@ -1044,11 +1079,14 @@ impl BTreeMap { /// assert_eq!(count["a"], 3); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { + pub fn entry(&mut self, key: K) -> Entry<'_, K, V> + where + K: Ord, + { // FIXME(@porglezomp) Avoid allocating if we don't insert let (map, dormant_map) = DormantMutRef::new(self); let root_node = Self::ensure_is_owned(&mut map.root).borrow_mut(); - match search::search_tree(root_node, &key) { + match root_node.search_tree(&key) { Found(handle) => Occupied(OccupiedEntry { handle, dormant_map, _marker: PhantomData }), GoDown(handle) => { Vacant(VacantEntry { key, handle, dormant_map, _marker: PhantomData }) @@ -1088,7 +1126,7 @@ impl BTreeMap { #[stable(feature = "btree_split_off", since = "1.11.0")] pub fn split_off(&mut self, key: &Q) -> Self where - K: Borrow, + K: Borrow + Ord, { if self.is_empty() { return Self::new(); @@ -1146,12 +1184,16 @@ impl BTreeMap { #[unstable(feature = "btree_drain_filter", issue = "70530")] pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, K, V, F> where + K: Ord, F: FnMut(&K, &mut V) -> bool, { DrainFilter { pred, inner: self.drain_filter_inner() } } - pub(super) fn drain_filter_inner(&mut self) -> DrainFilterInner<'_, K, V> { + pub(super) fn drain_filter_inner(&mut self) -> DrainFilterInner<'_, K, V> + where + K: Ord, + { if let Some(root) = self.root.as_mut() { let (root, dormant_root) = DormantMutRef::new(root); let front = root.borrow_mut().first_leaf_edge(); @@ -1184,7 +1226,10 @@ impl BTreeMap { /// ``` #[inline] #[unstable(feature = "map_into_keys_values", issue = "75294")] - pub fn into_keys(self) -> IntoKeys { + pub fn into_keys(self) -> IntoKeys + where + K: Ord, + { IntoKeys { inner: self.into_iter() } } @@ -1207,7 +1252,10 @@ impl BTreeMap { /// ``` #[inline] #[unstable(feature = "map_into_keys_values", issue = "75294")] - pub fn into_values(self) -> IntoValues { + pub fn into_values(self) -> IntoValues + where + K: Ord, + { IntoValues { inner: self.into_iter() } } } @@ -1361,7 +1409,7 @@ impl IntoIterator for BTreeMap { fn into_iter(self) -> IntoIter { let mut me = ManuallyDrop::new(self); if let Some(root) = me.root.take() { - let (f, b) = root.full_range(); + let (f, b) = root.into_dying().full_range(); IntoIter { front: Some(f), back: Some(b), length: me.length } } else { @@ -1964,9 +2012,9 @@ impl Debug for BTreeMap { } #[stable(feature = "rust1", since = "1.0.0")] -impl Index<&Q> for BTreeMap +impl Index<&Q> for BTreeMap where - K: Borrow, + K: Borrow + Ord, Q: Ord, { type Output = V; @@ -2132,6 +2180,7 @@ impl BTreeMap { /// a.insert(1, "a"); /// assert_eq!(a.len(), 1); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] pub const fn len(&self) -> usize { diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 90bef37f71..78edf11d39 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -111,6 +111,18 @@ impl BTreeMap { } } } + + // Transform the tree to minimize wasted space, obtaining fewer nodes that + // are mostly filled up to their capacity. The same compact tree could have + // been obtained by inserting keys in a shrewd order. + fn compact(&mut self) + where + K: Ord, + { + let iter = mem::take(self).into_iter(); + let root = BTreeMap::ensure_is_owned(&mut self.root); + root.bulk_push(iter, &mut self.length); + } } impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { @@ -765,7 +777,7 @@ fn test_range_backwards_4() { #[test] #[should_panic] -fn test_range_backwards_5() { +fn test_range_finding_ill_order_in_map() { let mut map = BTreeMap::new(); map.insert(Cyclic3::B, ()); // Lacking static_assert, call `range` conditionally, to emphasise that @@ -776,6 +788,47 @@ fn test_range_backwards_5() { } } +#[test] +#[should_panic] +fn test_range_finding_ill_order_in_range_ord() { + // Has proper order the first time asked, then flips around. + struct EvilTwin(i32); + + impl PartialOrd for EvilTwin { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + static COMPARES: AtomicUsize = AtomicUsize::new(0); + impl Ord for EvilTwin { + fn cmp(&self, other: &Self) -> Ordering { + let ord = self.0.cmp(&other.0); + if COMPARES.fetch_add(1, SeqCst) > 0 { ord.reverse() } else { ord } + } + } + + impl PartialEq for EvilTwin { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } + } + + impl Eq for EvilTwin {} + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + struct CompositeKey(i32, EvilTwin); + + impl Borrow for CompositeKey { + fn borrow(&self) -> &EvilTwin { + &self.1 + } + } + + let map = (0..12).map(|i| (CompositeKey(i, EvilTwin(i)), ())).collect::>(); + map.range(EvilTwin(5)..=EvilTwin(7)); +} + #[test] fn test_range_1000() { // Miri is too slow @@ -1210,6 +1263,51 @@ fn test_borrow() { map.insert(Rc::new(0), 1); assert_eq!(map[&0], 1); } + + #[allow(dead_code)] + fn get(v: &BTreeMap, ()>, t: &T) { + v.get(t); + } + + #[allow(dead_code)] + fn get_mut(v: &mut BTreeMap, ()>, t: &T) { + v.get_mut(t); + } + + #[allow(dead_code)] + fn get_key_value(v: &BTreeMap, ()>, t: &T) { + v.get_key_value(t); + } + + #[allow(dead_code)] + fn contains_key(v: &BTreeMap, ()>, t: &T) { + v.contains_key(t); + } + + #[allow(dead_code)] + fn range(v: &BTreeMap, ()>, t: T) { + v.range(t..); + } + + #[allow(dead_code)] + fn range_mut(v: &mut BTreeMap, ()>, t: T) { + v.range_mut(t..); + } + + #[allow(dead_code)] + fn remove(v: &mut BTreeMap, ()>, t: &T) { + v.remove(t); + } + + #[allow(dead_code)] + fn remove_entry(v: &mut BTreeMap, ()>, t: &T) { + v.remove_entry(t); + } + + #[allow(dead_code)] + fn split_off(v: &mut BTreeMap, ()>, t: &T) { + v.split_off(t); + } } #[test] @@ -1608,6 +1706,34 @@ fn test_send() { } } +#[allow(dead_code)] +fn test_ord_absence() { + fn map(mut map: BTreeMap) { + map.is_empty(); + map.len(); + map.iter(); + map.iter_mut(); + map.keys(); + map.values(); + map.values_mut(); + map.into_iter(); + } + + fn map_debug(mut map: BTreeMap) { + format!("{:?}", map); + format!("{:?}", map.iter()); + format!("{:?}", map.iter_mut()); + format!("{:?}", map.keys()); + format!("{:?}", map.values()); + format!("{:?}", map.values_mut()); + format!("{:?}", map.into_iter()); + } + + fn map_clone(mut map: BTreeMap) { + map.clone_from(&map.clone()); + } +} + #[allow(dead_code)] fn test_const() { const MAP: &'static BTreeMap<(), ()> = &BTreeMap::new(); @@ -1679,17 +1805,29 @@ fn test_first_last_entry() { } #[test] -fn test_insert_into_full_left() { - let mut map: BTreeMap<_, _> = (0..NODE_CAPACITY).map(|i| (i * 2, ())).collect(); - assert!(map.insert(NODE_CAPACITY, ()).is_none()); - map.check(); +fn test_insert_into_full_height_0() { + let size = NODE_CAPACITY; + for pos in 0..=size { + let mut map: BTreeMap<_, _> = (0..size).map(|i| (i * 2 + 1, ())).collect(); + assert!(map.insert(pos * 2, ()).is_none()); + map.check(); + } } #[test] -fn test_insert_into_full_right() { - let mut map: BTreeMap<_, _> = (0..NODE_CAPACITY).map(|i| (i * 2, ())).collect(); - assert!(map.insert(NODE_CAPACITY + 2, ()).is_none()); - map.check(); +fn test_insert_into_full_height_1() { + let size = NODE_CAPACITY + 1 + NODE_CAPACITY; + for pos in 0..=size { + let mut map: BTreeMap<_, _> = (0..size).map(|i| (i * 2 + 1, ())).collect(); + map.compact(); + let root_node = map.root.as_ref().unwrap().reborrow(); + assert_eq!(root_node.len(), 1); + assert_eq!(root_node.first_leaf_edge().into_node().len(), NODE_CAPACITY); + assert_eq!(root_node.last_leaf_edge().into_node().len(), NODE_CAPACITY); + + assert!(map.insert(pos * 2, ()).is_none()); + map.check(); + } } macro_rules! create_append_test { @@ -1797,7 +1935,6 @@ fn test_append_ord_chaos() { } fn rand_data(len: usize) -> Vec<(u32, u32)> { - assert!(len * 2 <= 70029); // from that point on numbers repeat let mut rng = DeterministicRng::new(); Vec::from_iter((0..len).map(|_| (rng.next(), rng.next()))) } @@ -1862,6 +1999,25 @@ fn test_split_off_tiny_right_height_2() { assert_eq!(*right.last_key_value().unwrap().0, last); } +#[test] +fn test_split_off_halfway() { + let mut rng = DeterministicRng::new(); + for &len in &[NODE_CAPACITY, 25, 50, 75, 100] { + let mut data = Vec::from_iter((0..len).map(|_| (rng.next(), ()))); + // Insertion in non-ascending order creates some variation in node length. + let mut map = BTreeMap::from_iter(data.iter().copied()); + data.sort(); + let small_keys = data.iter().take(len / 2).map(|kv| kv.0); + let large_keys = data.iter().skip(len / 2).map(|kv| kv.0); + let split_key = large_keys.clone().next().unwrap(); + let right = map.split_off(&split_key); + map.check(); + right.check(); + assert!(map.keys().copied().eq(small_keys)); + assert!(right.keys().copied().eq(large_keys)); + } +} + #[test] fn test_split_off_large_random_sorted() { // Miri is too slow diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index ebcbb0e467..cdb3910404 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -38,6 +38,7 @@ pub unsafe fn unwrap_unchecked(val: Option) -> T { #[cfg(test)] /// XorShiftRng struct DeterministicRng { + count: usize, x: u32, y: u32, z: u32, @@ -47,11 +48,13 @@ struct DeterministicRng { #[cfg(test)] impl DeterministicRng { fn new() -> Self { - DeterministicRng { x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } + DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } } - /// Guarantees that the first 70029 results are unique. + /// Guarantees that each returned number is unique. fn next(&mut self) -> u32 { + self.count += 1; + assert!(self.count <= 70029); let x = self.x; let t = x ^ (x << 11); self.x = self.y; diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index cce8b21a2b..2773b427fb 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -5,14 +5,14 @@ use core::ops::RangeBounds; use core::ptr; use super::node::{marker, ForceResult::*, Handle, NodeRef}; -use super::search::{self, SearchResult}; +use super::search::SearchResult; use super::unwrap_unchecked; /// Finds the leaf edges delimiting a specified range in or underneath a node. /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. -fn range_search( +fn range_search( root1: NodeRef, root2: NodeRef, range: R, @@ -25,7 +25,12 @@ where K: Borrow, R: RangeBounds, { - match (range.start_bound(), range.end_bound()) { + // WARNING: Inlining these variables would be unsound (#81138) + // We assume the bounds reported by `range` remain the same, but + // an adversarial implementation could change between calls + let start = range.start_bound(); + let end = range.end_bound(); + match (start, end) { (Excluded(s), Excluded(e)) if s == e => { panic!("range start and end are equal and excluded in BTreeMap") } @@ -41,15 +46,16 @@ where let mut max_found = false; loop { - let front = match (min_found, range.start_bound()) { - (false, Included(key)) => match search::search_node(min_node, key) { + // Using `range` again would be unsound (#81138) + let front = match (min_found, start) { + (false, Included(key)) => match min_node.search_node(key) { SearchResult::Found(kv) => { min_found = true; kv.left_edge() } SearchResult::GoDown(edge) => edge, }, - (false, Excluded(key)) => match search::search_node(min_node, key) { + (false, Excluded(key)) => match min_node.search_node(key) { SearchResult::Found(kv) => { min_found = true; kv.right_edge() @@ -61,15 +67,16 @@ where (_, Unbounded) => min_node.first_edge(), }; - let back = match (max_found, range.end_bound()) { - (false, Included(key)) => match search::search_node(max_node, key) { + // Using `range` again would be unsound (#81138) + let back = match (max_found, end) { + (false, Included(key)) => match max_node.search_node(key) { SearchResult::Found(kv) => { max_found = true; kv.right_edge() } SearchResult::GoDown(edge) => edge, }, - (false, Excluded(key)) => match search::search_node(max_node, key) { + (false, Excluded(key)) => match max_node.search_node(key) { SearchResult::Found(kv) => { max_found = true; kv.left_edge() @@ -98,7 +105,7 @@ where } /// Equivalent to `range_search(k, v, ..)` but without the `Ord` bound. -fn full_range( +fn full_range( root1: NodeRef, root2: NodeRef, ) -> ( @@ -195,15 +202,15 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } } -impl NodeRef { +impl NodeRef { /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing massively destructive mutation, so must be /// used with the utmost care. pub fn full_range( self, ) -> ( - Handle, marker::Edge>, - Handle, marker::Edge>, + Handle, marker::Edge>, + Handle, marker::Edge>, ) { // We duplicate the root NodeRef here -- we will never access it in a way // that overlaps references obtained from the root. @@ -212,7 +219,9 @@ impl NodeRef { } } -impl Handle, marker::Edge> { +impl + Handle, marker::Edge> +{ /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the right side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the last one in the tree, returns [`Result::Err`] with the root node. @@ -256,7 +265,9 @@ impl Handle, marker::E } } -impl Handle, marker::Edge> { +impl + Handle, marker::Edge> +{ /// Given an internal edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the right side, which is either in the same internal node or in an ancestor node. /// If the internal edge is the last one in the tree, returns [`Result::Err`] with the root node. @@ -290,8 +301,8 @@ macro_rules! def_next_kv_uncheched_dealloc { /// - The node carrying the next KV returned must not have been deallocated by a /// previous call on any handle obtained for this tree. unsafe fn $name ( - leaf_edge: Handle, marker::Edge>, - ) -> Handle, marker::KV> { + leaf_edge: Handle, marker::Edge>, + ) -> Handle, marker::KV> { let mut edge = leaf_edge.forget_node_type(); loop { edge = match edge.$adjacent_kv() { @@ -371,7 +382,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::E } } -impl Handle, marker::Edge> { +impl Handle, marker::Edge> { /// Moves the leaf edge handle to the next leaf edge and returns the key and value /// in between, deallocating any node left behind while leaving the corresponding /// edge in its parent node dangling. @@ -415,7 +426,7 @@ impl Handle, marker::Edge> { } } -impl NodeRef { +impl NodeRef { /// Returns the leftmost leaf edge in or underneath a node - in other words, the edge /// you need first when navigating forward (or last when navigating backward). #[inline] @@ -496,7 +507,9 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } } -impl Handle, marker::KV> { +impl + Handle, marker::KV> +{ /// Returns the leaf edge closest to a KV for forward navigation. pub fn next_leaf_edge(self) -> Handle, marker::Edge> { match self.force() { diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 769383515b..1d632512c7 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -93,8 +93,8 @@ struct InternalNode { data: LeafNode, /// The pointers to the children of this node. `len + 1` of these are considered - /// initialized and valid. Although during the process of `into_iter` or `drop`, - /// some pointers are dangling while others still need to be traversed. + /// initialized and valid, except that near the end, while the tree is held + /// through borrow type `Dying`, some of these pointers are dangling. edges: [MaybeUninit>; 2 * B], } @@ -119,7 +119,7 @@ impl InternalNode { /// is not a separate type and has no destructor. type BoxedNode = NonNull>; -/// An owned tree. +/// The root node of an owned tree. /// /// Note that this does not have a destructor, and must be cleaned up manually. pub type Root = NodeRef; @@ -142,24 +142,38 @@ impl NodeRef { } impl NodeRef { + fn new_internal(child: Root) -> Self { + let mut new_node = Box::new(unsafe { InternalNode::new() }); + new_node.edges[0].write(child.node); + NodeRef::from_new_internal(new_node, child.height + 1) + } + fn from_new_internal(internal: Box>, height: usize) -> Self { - NodeRef { height, node: NonNull::from(Box::leak(internal)).cast(), _marker: PhantomData } + let node = NonNull::from(Box::leak(internal)).cast(); + let mut this = NodeRef { height, node, _marker: PhantomData }; + this.borrow_mut().correct_all_childrens_parent_links(); + this } } impl NodeRef { - /// Mutably borrows the owned node. Unlike `reborrow_mut`, this is safe, - /// because the return value cannot be used to destroy the node itself, - /// and there cannot be other references to the tree (except during the - /// process of `into_iter` or `drop`, but that is horrific already). + /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe + /// because the return value cannot be used to destroy the root, and there + /// cannot be other references to the tree. pub fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } - /// Slightly mutably borrows the owned node. + /// Slightly mutably borrows the owned root node. pub fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } + + /// Irreversibly transistions to a reference that offers traversal, + /// destructive methods and little else. + pub fn into_dying(self) -> NodeRef { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } } impl NodeRef { @@ -167,11 +181,7 @@ impl NodeRef { /// make that new node the root node, and return it. This increases the height by 1 /// and is the opposite of `pop_internal_level`. pub fn push_internal_level(&mut self) -> NodeRef, K, V, marker::Internal> { - let mut new_node = Box::new(unsafe { InternalNode::new() }); - new_node.edges[0].write(self.node); - let mut new_root = NodeRef::from_new_internal(new_node, self.height + 1); - new_root.borrow_mut().first_edge().correct_parent_link(); - *self = new_root.forget_type(); + super::mem::take_mut(self, |old_root| NodeRef::new_internal(old_root).forget_type()); // `self.borrow_mut()`, except that we just forgot we're internal now: NodeRef { height: self.height, node: self.node, _marker: PhantomData } @@ -179,7 +189,7 @@ impl NodeRef { /// Removes the internal root node, using its first child as the new root node. /// As it is intended only to be called when the root node has only one child, - /// no cleanup is done on any of the other children. + /// no cleanup is done on any of the keys, values and other children. /// This decreases the height by 1 and is the opposite of `push_internal_level`. /// /// Requires exclusive access to the `Root` object but not to the root node; @@ -191,8 +201,13 @@ impl NodeRef { let top = self.node; - let internal_node = NodeRef { height: self.height, node: top, _marker: PhantomData }; - *self = internal_node.first_edge().descend(); + // SAFETY: we asserted to be internal. + let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() }; + // SAFETY: we borrowed `self` exclusively and its borrow type is exclusive. + let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) }; + // SAFETY: the first edge is always initialized. + self.node = unsafe { internal_node.edges[0].assume_init_read() }; + self.height -= 1; self.clear_parent_link(); unsafe { @@ -219,8 +234,11 @@ impl NodeRef { /// although insert methods allow a mutable pointer to a value to coexist. /// - When this is `Owned`, the `NodeRef` acts roughly like `Box`, /// but does not have a destructor, and must be cleaned up manually. +/// - When this is `Dying`, the `NodeRef` still acts roughly like `Box`, +/// but has methods to destroy the tree bit by bit, and ordinary methods, +/// while not marked as unsafe to call, can invoke UB if called incorrectly. /// Since any `NodeRef` allows navigating through the tree, `BorrowType` -/// effectively applies to the entire tree, not just the node itself. +/// effectively applies to the entire tree, not just to the node itself. /// - `K` and `V`: These are the types of keys and values stored in the nodes. /// - `Type`: This can be `Leaf`, `Internal`, or `LeafOrInternal`. When this is /// `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the @@ -233,13 +251,12 @@ impl NodeRef { /// such restrictions: /// - For each type parameter, we can only define a method either generically /// or for one particular type. For example, we cannot define a method like -/// `key_at` generically for all `BorrowType`, because we want it to return -/// `&'a K` for most choices of `BorrowType`, but plain `K` for `Owned`. -/// We cannot define `key_at` once for all types that carry a lifetime. +/// `into_kv` generically for all `BorrowType`, or once for all types that +/// carry a lifetime, because we want it to return `&'a` references. /// Therefore, we define it only for the least powerful type `Immut<'a>`. /// - We cannot get implicit coercion from say `Mut<'a>` to `Immut<'a>`. /// Therefore, we have to explicitly call `reborrow` on a more powerfull -/// `NodeRef` in order to reach a method like `key_at`. +/// `NodeRef` in order to reach a method like `into_kv`. /// /// All methods on `NodeRef` that return some kind of reference, either: /// - Take `self` by value, and return the lifetime carried by `BorrowType`. @@ -276,6 +293,7 @@ unsafe impl<'a, K: Sync + 'a, V: Sync + 'a, Type> Send for NodeRef Send for NodeRef, K, V, Type> {} unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef, K, V, Type> {} unsafe impl Send for NodeRef {} +unsafe impl Send for NodeRef {} impl NodeRef { /// Unpack a node reference that was packed as `NodeRef::parent`. @@ -295,15 +313,6 @@ impl NodeRef { } } -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - /// Exposes the data of an internal node in an immutable tree. - fn as_internal(this: &Self) -> &'a InternalNode { - let ptr = Self::as_internal_ptr(this); - // SAFETY: there can be no mutable references into this tree borrowed as `Immut`. - unsafe { &*ptr } - } -} - impl<'a, K, V> NodeRef, K, V, marker::Internal> { /// Borrows exclusive access to the data of an internal node. fn as_internal_mut(&mut self) -> &mut InternalNode { @@ -348,38 +357,7 @@ impl NodeRef { } } -impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// Exposes one of the keys stored in the node. - /// - /// # Safety - /// The node has more than `idx` initialized elements. - pub unsafe fn key_at(self, idx: usize) -> &'a K { - debug_assert!(idx < self.len()); - unsafe { self.into_leaf().keys.get_unchecked(idx).assume_init_ref() } - } - - /// Exposes one of the values stored in the node. - /// - /// # Safety - /// The node has more than `idx` initialized elements. - unsafe fn val_at(self, idx: usize) -> &'a V { - debug_assert!(idx < self.len()); - unsafe { self.into_leaf().vals.get_unchecked(idx).assume_init_ref() } - } -} - -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - /// Exposes the contents of one of the edges in the node. - /// - /// # Safety - /// The node has more than `idx` initialized elements. - unsafe fn edge_at(self, idx: usize) -> &'a BoxedNode { - debug_assert!(idx <= self.len()); - unsafe { Self::as_internal(&self).edges.get_unchecked(idx).assume_init_ref() } - } -} - -impl NodeRef { +impl NodeRef { /// Finds the parent of the current node. Returns `Ok(handle)` if the current /// node actually has a parent, where `handle` points to the edge of the parent /// that points to the current node. Returns `Err(self)` if the current node has @@ -392,6 +370,7 @@ impl NodeRef { pub fn ascend( self, ) -> Result, marker::Edge>, Self> { + assert!(BorrowType::PERMITS_TRAVERSAL); // We need to use raw pointers to nodes because, if BorrowType is marker::ValMut, // there might be outstanding mutable references to values that we must not invalidate. let leaf_ptr: *const _ = Self::as_leaf_ptr(&self); @@ -436,15 +415,23 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { // SAFETY: there can be no mutable references into this tree borrowed as `Immut`. unsafe { &*ptr } } + + /// Borrows a view into the keys stored in the node. + pub fn keys(&self) -> &[K] { + let leaf = self.into_leaf(); + unsafe { + MaybeUninit::slice_assume_init_ref(leaf.keys.get_unchecked(..usize::from(leaf.len))) + } + } } -impl NodeRef { +impl NodeRef { /// Similar to `ascend`, gets a reference to a node's parent node, but also - /// deallocate the current node in the process. This is unsafe because the + /// deallocates the current node in the process. This is unsafe because the /// current node will still be accessible despite being deallocated. pub unsafe fn deallocate_and_ascend( self, - ) -> Option, marker::Edge>> { + ) -> Option, marker::Edge>> { let height = self.height; let node = self.node; let ret = self.ascend().ok(); @@ -509,7 +496,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// /// # Safety /// `index` is in bounds of 0..CAPACITY - unsafe fn key_area_mut_at(&mut self, index: I) -> &mut Output + unsafe fn key_area_mut(&mut self, index: I) -> &mut Output where I: SliceIndex<[MaybeUninit], Output = Output>, { @@ -523,7 +510,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// /// # Safety /// `index` is in bounds of 0..CAPACITY - unsafe fn val_area_mut_at(&mut self, index: I) -> &mut Output + unsafe fn val_area_mut(&mut self, index: I) -> &mut Output where I: SliceIndex<[MaybeUninit], Output = Output>, { @@ -539,7 +526,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { /// /// # Safety /// `index` is in bounds of 0..CAPACITY + 1 - unsafe fn edge_area_mut_at(&mut self, index: I) -> &mut Output + unsafe fn edge_area_mut(&mut self, index: I) -> &mut Output where I: SliceIndex<[MaybeUninit>], Output = Output>, { @@ -550,31 +537,6 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { } } -impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// Exposes the entire key storage area in the node, - /// regardless of the node's current length, - /// having exclusive access to the entire node. - unsafe fn key_area(self) -> &'a [MaybeUninit] { - self.into_leaf().keys.as_slice() - } - - /// Exposes the entire value storage area in the node, - /// regardless of the node's current length, - /// having exclusive access to the entire node. - unsafe fn val_area(self) -> &'a [MaybeUninit] { - self.into_leaf().vals.as_slice() - } -} - -impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { - /// Exposes the entire storage area for edge contents in the node, - /// regardless of the node's current length, - /// having exclusive access to the entire node. - unsafe fn edge_area(self) -> &'a [MaybeUninit>] { - Self::as_internal(&self).edges.as_slice() - } -} - impl<'a, K, V, Type> NodeRef, K, V, Type> { /// # Safety /// - The node has more than `idx` initialized elements. @@ -583,8 +545,8 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { // to avoid aliasing with outstanding references to other elements, // in particular, those returned to the caller in earlier iterations. let leaf = Self::as_leaf_ptr(&mut self); - let keys = unsafe { &raw const (*leaf).keys }; - let vals = unsafe { &raw mut (*leaf).vals }; + let keys = unsafe { ptr::addr_of!((*leaf).keys) }; + let vals = unsafe { ptr::addr_of_mut!((*leaf).vals) }; // We must coerce to unsized array pointers because of Rust issue #74679. let keys: *const [_] = keys; let vals: *mut [_] = vals; @@ -628,19 +590,8 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { assert!(idx < CAPACITY); *len += 1; unsafe { - self.key_area_mut_at(idx).write(key); - self.val_area_mut_at(idx).write(val); - } - } - - /// Adds a key-value pair to the beginning of the node. - fn push_front(&mut self, key: K, val: V) { - let new_len = self.len() + 1; - assert!(new_len <= CAPACITY); - unsafe { - slice_insert(self.key_area_mut_at(..new_len), 0, key); - slice_insert(self.val_area_mut_at(..new_len), 0, val); - *self.len_mut() = new_len as u16; + self.key_area_mut(idx).write(key); + self.val_area_mut(idx).write(val); } } } @@ -672,98 +623,12 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { assert!(idx < CAPACITY); *len += 1; unsafe { - self.key_area_mut_at(idx).write(key); - self.val_area_mut_at(idx).write(val); - self.edge_area_mut_at(idx + 1).write(edge.node); + self.key_area_mut(idx).write(key); + self.val_area_mut(idx).write(val); + self.edge_area_mut(idx + 1).write(edge.node); Handle::new_edge(self.reborrow_mut(), idx + 1).correct_parent_link(); } } - - /// Adds a key-value pair, and an edge to go to the left of that pair, - /// to the beginning of the node. - fn push_front(&mut self, key: K, val: V, edge: Root) { - let new_len = self.len() + 1; - assert!(edge.height == self.height - 1); - assert!(new_len <= CAPACITY); - - unsafe { - slice_insert(self.key_area_mut_at(..new_len), 0, key); - slice_insert(self.val_area_mut_at(..new_len), 0, val); - slice_insert(self.edge_area_mut_at(..new_len + 1), 0, edge.node); - *self.len_mut() = new_len as u16; - } - - self.correct_all_childrens_parent_links(); - } -} - -impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { - /// Removes a key-value pair from the end of the node and returns the pair. - /// Also removes the edge that was to the right of that pair and, if the node - /// is internal, returns the orphaned subtree that this edge owned. - fn pop(&mut self) -> (K, V, Option>) { - debug_assert!(self.len() > 0); - - let idx = self.len() - 1; - - unsafe { - let key = ptr::read(self.reborrow().key_at(idx)); - let val = ptr::read(self.reborrow().val_at(idx)); - let edge = match self.reborrow_mut().force() { - ForceResult::Leaf(_) => None, - ForceResult::Internal(internal) => { - let node = ptr::read(internal.reborrow().edge_at(idx + 1)); - let mut edge = Root { node, height: internal.height - 1, _marker: PhantomData }; - // Currently, clearing the parent link is superfluous, because we will - // insert the node elsewhere and set its parent link again. - edge.clear_parent_link(); - Some(edge) - } - }; - - *self.len_mut() -= 1; - (key, val, edge) - } - } - - /// Removes a key-value pair from the beginning of the node and returns the pair. - /// Also removes the edge that was to the left of that pair and, if the node is - /// internal, returns the orphaned subtree that this edge owned. - fn pop_front(&mut self) -> (K, V, Option>) { - debug_assert!(self.len() > 0); - - let old_len = self.len(); - - unsafe { - let key = slice_remove(self.key_area_mut_at(..old_len), 0); - let val = slice_remove(self.val_area_mut_at(..old_len), 0); - let edge = match self.reborrow_mut().force() { - ForceResult::Leaf(_) => None, - ForceResult::Internal(mut internal) => { - let node = slice_remove(internal.edge_area_mut_at(..old_len + 1), 0); - let mut edge = Root { node, height: internal.height - 1, _marker: PhantomData }; - // Currently, clearing the parent link is superfluous, because we will - // insert the node elsewhere and set its parent link again. - edge.clear_parent_link(); - - internal.correct_childrens_parent_links(0..old_len); - - Some(edge) - } - }; - - *self.len_mut() -= 1; - - (key, val, edge) - } - } - - fn into_kv_pointers_mut(mut self) -> (*mut K, *mut V) { - let leaf = self.as_leaf_mut(); - let keys = MaybeUninit::slice_as_mut_ptr(&mut leaf.keys); - let vals = MaybeUninit::slice_as_mut_ptr(&mut leaf.vals); - (keys, vals) - } } impl NodeRef { @@ -964,11 +829,11 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark let new_len = self.node.len() + 1; unsafe { - slice_insert(self.node.key_area_mut_at(..new_len), self.idx, key); - slice_insert(self.node.val_area_mut_at(..new_len), self.idx, val); + slice_insert(self.node.key_area_mut(..new_len), self.idx, key); + slice_insert(self.node.val_area_mut(..new_len), self.idx, val); *self.node.len_mut() = new_len as u16; - self.node.val_area_mut_at(self.idx).assume_init_mut() + self.node.val_area_mut(self.idx).assume_init_mut() } } } @@ -1023,9 +888,9 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, let new_len = self.node.len() + 1; unsafe { - slice_insert(self.node.key_area_mut_at(..new_len), self.idx, key); - slice_insert(self.node.val_area_mut_at(..new_len), self.idx, val); - slice_insert(self.node.edge_area_mut_at(..new_len + 1), self.idx + 1, edge.node); + slice_insert(self.node.key_area_mut(..new_len), self.idx, key); + slice_insert(self.node.val_area_mut(..new_len), self.idx, val); + slice_insert(self.node.edge_area_mut(..new_len + 1), self.idx + 1, edge.node); *self.node.len_mut() = new_len as u16; self.node.correct_childrens_parent_links(self.idx + 1..new_len + 1); @@ -1101,7 +966,9 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark } } -impl Handle, marker::Edge> { +impl + Handle, marker::Edge> +{ /// Finds the node pointed to by this edge. /// /// The method name assumes you picture trees with the root node on top. @@ -1109,6 +976,7 @@ impl Handle, marke /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. pub fn descend(self) -> NodeRef { + assert!(BorrowType::PERMITS_TRAVERSAL); // We need to use raw pointers to nodes because, if BorrowType is // marker::ValMut, there might be outstanding mutable references to // values that we must not invalidate. There's no worry accessing the @@ -1124,16 +992,21 @@ impl Handle, marke impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { pub fn into_kv(self) -> (&'a K, &'a V) { - (unsafe { self.node.key_at(self.idx) }, unsafe { self.node.val_at(self.idx) }) + debug_assert!(self.idx < self.node.len()); + let leaf = self.node.into_leaf(); + let k = unsafe { leaf.keys.get_unchecked(self.idx).assume_init_ref() }; + let v = unsafe { leaf.vals.get_unchecked(self.idx).assume_init_ref() }; + (k, v) } } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { pub fn key_mut(&mut self) -> &mut K { - unsafe { self.node.key_area_mut_at(self.idx).assume_init_mut() } + unsafe { self.node.key_area_mut(self.idx).assume_init_mut() } } pub fn into_val_mut(self) -> &'a mut V { + debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() } } @@ -1147,6 +1020,7 @@ impl<'a, K, V, NodeType> Handle, K, V, NodeType>, mar impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { pub fn kv_mut(&mut self) -> (&mut K, &mut V) { + debug_assert!(self.idx < self.node.len()); // We cannot call separate key and value methods, because calling the second one // invalidates the reference returned by the first. unsafe { @@ -1169,21 +1043,20 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> /// by taking care of leaf data. fn split_leaf_data(&mut self, new_node: &mut LeafNode) -> (K, V) { debug_assert!(self.idx < self.node.len()); - let new_len = self.node.len() - self.idx - 1; + let old_len = self.node.len(); + let new_len = old_len - self.idx - 1; new_node.len = new_len as u16; unsafe { - let k = ptr::read(self.node.reborrow().key_at(self.idx)); - let v = ptr::read(self.node.reborrow().val_at(self.idx)); + let k = self.node.key_area_mut(self.idx).assume_init_read(); + let v = self.node.val_area_mut(self.idx).assume_init_read(); - ptr::copy_nonoverlapping( - self.node.reborrow().key_area().as_ptr().add(self.idx + 1), - new_node.keys.as_mut_ptr(), - new_len, + move_to_slice( + self.node.key_area_mut(self.idx + 1..old_len), + &mut new_node.keys[..new_len], ); - ptr::copy_nonoverlapping( - self.node.reborrow().val_area().as_ptr().add(self.idx + 1), - new_node.vals.as_mut_ptr(), - new_len, + move_to_slice( + self.node.val_area_mut(self.idx + 1..old_len), + &mut new_node.vals[..new_len], ); *self.node.len_mut() = self.idx as u16; @@ -1218,8 +1091,8 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { let old_len = self.node.len(); unsafe { - let k = slice_remove(self.node.key_area_mut_at(..old_len), self.idx); - let v = slice_remove(self.node.val_area_mut_at(..old_len), self.idx); + let k = slice_remove(self.node.key_area_mut(..old_len), self.idx); + let v = slice_remove(self.node.val_area_mut(..old_len), self.idx); *self.node.len_mut() = (old_len - 1) as u16; ((k, v), self.left_edge()) } @@ -1235,20 +1108,18 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// - All the edges and key-value pairs to the right of this handle are put into /// a newly allocated node. pub fn split(mut self) -> SplitResult<'a, K, V, marker::Internal> { + let old_len = self.node.len(); unsafe { let mut new_node = Box::new(InternalNode::new()); let kv = self.split_leaf_data(&mut new_node.data); let new_len = usize::from(new_node.data.len); - ptr::copy_nonoverlapping( - self.node.reborrow().edge_area().as_ptr().add(self.idx + 1), - new_node.edges.as_mut_ptr(), - new_len + 1, + move_to_slice( + self.node.edge_area_mut(self.idx + 1..old_len + 1), + &mut new_node.edges[..new_len + 1], ); let height = self.node.height; - let mut right = NodeRef::from_new_internal(new_node, height); - - right.borrow_mut().correct_childrens_parent_links(0..=new_len); + let right = NodeRef::from_new_internal(new_node, height); SplitResult { left: self.node, kv, right } } @@ -1329,62 +1200,53 @@ impl<'a, K, V> BalancingContext<'a, K, V> { self.right_child } - /// Returns `true` if it is valid to call `.merge()` in the balancing context, - /// i.e., whether there is enough room in a node to hold the combination of - /// both adjacent child nodes, along with the key-value pair in the parent. + /// Returns whether merging is possible, i.e., whether there is enough room + /// in a node to combine the central KV with both adjacent child nodes. pub fn can_merge(&self) -> bool { self.left_child.len() + 1 + self.right_child.len() <= CAPACITY } } impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { - /// Merges the parent's key-value pair and both adjacent child nodes into - /// the left node and returns an edge handle in that expanded left node. - /// If `track_edge_idx` is given some value, the returned edge corresponds - /// to where the edge in that child node ended up, - /// - /// Panics unless we `.can_merge()`. - pub fn merge( + /// Performs a merge and lets a closure decide what to return. + fn do_merge< + F: FnOnce( + NodeRef, K, V, marker::Internal>, + NodeRef, K, V, marker::LeafOrInternal>, + ) -> R, + R, + >( self, - track_edge_idx: Option>, - ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { + result: F, + ) -> R { let Handle { node: mut parent_node, idx: parent_idx, _marker } = self.parent; let old_parent_len = parent_node.len(); let mut left_node = self.left_child; let old_left_len = left_node.len(); - let right_node = self.right_child; + let mut right_node = self.right_child; let right_len = right_node.len(); let new_left_len = old_left_len + 1 + right_len; assert!(new_left_len <= CAPACITY); - assert!(match track_edge_idx { - None => true, - Some(LeftOrRight::Left(idx)) => idx <= old_left_len, - Some(LeftOrRight::Right(idx)) => idx <= right_len, - }); unsafe { *left_node.len_mut() = new_left_len as u16; - let parent_key = - slice_remove(parent_node.key_area_mut_at(..old_parent_len), parent_idx); - left_node.key_area_mut_at(old_left_len).write(parent_key); - ptr::copy_nonoverlapping( - right_node.reborrow().key_area().as_ptr(), - left_node.key_area_mut_at(old_left_len + 1..).as_mut_ptr(), - right_len, + let parent_key = slice_remove(parent_node.key_area_mut(..old_parent_len), parent_idx); + left_node.key_area_mut(old_left_len).write(parent_key); + move_to_slice( + right_node.key_area_mut(..right_len), + left_node.key_area_mut(old_left_len + 1..new_left_len), ); - let parent_val = - slice_remove(parent_node.val_area_mut_at(..old_parent_len), parent_idx); - left_node.val_area_mut_at(old_left_len).write(parent_val); - ptr::copy_nonoverlapping( - right_node.reborrow().val_area().as_ptr(), - left_node.val_area_mut_at(old_left_len + 1..).as_mut_ptr(), - right_len, + let parent_val = slice_remove(parent_node.val_area_mut(..old_parent_len), parent_idx); + left_node.val_area_mut(old_left_len).write(parent_val); + move_to_slice( + right_node.val_area_mut(..right_len), + left_node.val_area_mut(old_left_len + 1..new_left_len), ); - slice_remove(&mut parent_node.edge_area_mut_at(..old_parent_len + 1), parent_idx + 1); + slice_remove(&mut parent_node.edge_area_mut(..old_parent_len + 1), parent_idx + 1); parent_node.correct_childrens_parent_links(parent_idx + 1..old_parent_len); *parent_node.len_mut() -= 1; @@ -1392,11 +1254,10 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { // SAFETY: the height of the nodes being merged is one below the height // of the node of this edge, thus above zero, so they are internal. let mut left_node = left_node.reborrow_mut().cast_to_internal_unchecked(); - let right_node = right_node.cast_to_internal_unchecked(); - ptr::copy_nonoverlapping( - right_node.reborrow().edge_area().as_ptr(), - left_node.edge_area_mut_at(old_left_len + 1..).as_mut_ptr(), - right_len + 1, + let mut right_node = right_node.cast_to_internal_unchecked(); + move_to_slice( + right_node.edge_area_mut(..right_len + 1), + left_node.edge_area_mut(old_left_len + 1..new_left_len + 1), ); left_node.correct_childrens_parent_links(old_left_len + 1..new_left_len + 1); @@ -1405,14 +1266,47 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } else { Global.deallocate(right_node.node.cast(), Layout::new::>()); } - - let new_idx = match track_edge_idx { - None => 0, - Some(LeftOrRight::Left(idx)) => idx, - Some(LeftOrRight::Right(idx)) => old_left_len + 1 + idx, - }; - Handle::new_edge(left_node, new_idx) } + result(parent_node, left_node) + } + + /// Merges the parent's key-value pair and both adjacent child nodes into + /// the left child node and returns the shrunk parent node. + /// + /// Panics unless we `.can_merge()`. + pub fn merge_tracking_parent(self) -> NodeRef, K, V, marker::Internal> { + self.do_merge(|parent, _child| parent) + } + + /// Merges the parent's key-value pair and both adjacent child nodes into + /// the left child node and returns that child node. + /// + /// Panics unless we `.can_merge()`. + pub fn merge_tracking_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + self.do_merge(|_parent, child| child) + } + + /// Merges the parent's key-value pair and both adjacent child nodes into + /// the left child node and returns the edge handle in that child node + /// where the tracked child edge ended up, + /// + /// Panics unless we `.can_merge()`. + pub fn merge_tracking_child_edge( + self, + track_edge_idx: LeftOrRight, + ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { + let old_left_len = self.left_child.len(); + let right_len = self.right_child.len(); + assert!(match track_edge_idx { + LeftOrRight::Left(idx) => idx <= old_left_len, + LeftOrRight::Right(idx) => idx <= right_len, + }); + let child = self.merge_tracking_child(); + let new_idx = match track_edge_idx { + LeftOrRight::Left(idx) => idx, + LeftOrRight::Right(idx) => old_left_len + 1 + idx, + }; + unsafe { Handle::new_edge(child, new_idx) } } /// Removes a key-value pair from the left child and places it in the key-value storage @@ -1423,18 +1317,8 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { mut self, track_right_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { - unsafe { - let (k, v, edge) = self.left_child.pop(); - - let (k, v) = self.parent.replace_kv(k, v); - - match self.right_child.reborrow_mut().force() { - ForceResult::Leaf(mut leaf) => leaf.push_front(k, v), - ForceResult::Internal(mut internal) => internal.push_front(k, v, edge.unwrap()), - } - - Handle::new_edge(self.right_child, 1 + track_right_edge_idx) - } + self.bulk_steal_left(1); + unsafe { Handle::new_edge(self.right_child, 1 + track_right_edge_idx) } } /// Removes a key-value pair from the right child and places it in the key-value storage @@ -1445,18 +1329,8 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { mut self, track_left_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { - unsafe { - let (k, v, edge) = self.right_child.pop_front(); - - let (k, v) = self.parent.replace_kv(k, v); - - match self.left_child.reborrow_mut().force() { - ForceResult::Leaf(mut leaf) => leaf.push(k, v), - ForceResult::Internal(mut internal) => internal.push(k, v, edge.unwrap()), - } - - Handle::new_edge(self.left_child, track_left_edge_idx) - } + self.bulk_steal_right(1); + unsafe { Handle::new_edge(self.left_child, track_left_edge_idx) } } /// This does stealing similar to `steal_left` but steals multiple elements at once. @@ -1479,37 +1353,42 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { // Move leaf data. { - let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); - let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); - let parent_kv = { - let kv = self.parent.kv_mut(); - (kv.0 as *mut K, kv.1 as *mut V) - }; - // Make room for stolen elements in the right child. - ptr::copy(right_kv.0, right_kv.0.add(count), old_right_len); - ptr::copy(right_kv.1, right_kv.1.add(count), old_right_len); + slice_shr(right_node.key_area_mut(..new_right_len), count); + slice_shr(right_node.val_area_mut(..new_right_len), count); // Move elements from the left child to the right one. - move_kv(left_kv, new_left_len + 1, right_kv, 0, count - 1); - - // Move parent's key-value pair to the right child. - move_kv(parent_kv, 0, right_kv, count - 1, 1); + move_to_slice( + left_node.key_area_mut(new_left_len + 1..old_left_len), + right_node.key_area_mut(..count - 1), + ); + move_to_slice( + left_node.val_area_mut(new_left_len + 1..old_left_len), + right_node.val_area_mut(..count - 1), + ); // Move the left-most stolen pair to the parent. - move_kv(left_kv, new_left_len, parent_kv, 0, 1); + let k = left_node.key_area_mut(new_left_len).assume_init_read(); + let v = left_node.val_area_mut(new_left_len).assume_init_read(); + let (k, v) = self.parent.replace_kv(k, v); + + // Move parent's key-value pair to the right child. + right_node.key_area_mut(count - 1).write(k); + right_node.val_area_mut(count - 1).write(v); } match (left_node.reborrow_mut().force(), right_node.reborrow_mut().force()) { - (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { + (ForceResult::Internal(mut left), ForceResult::Internal(mut right)) => { // Make room for stolen edges. - let left = left.reborrow(); - let right_edges = right.edge_area_mut_at(..).as_mut_ptr(); - ptr::copy(right_edges, right_edges.add(count), old_right_len + 1); - right.correct_childrens_parent_links(count..new_right_len + 1); + slice_shr(right.edge_area_mut(..new_right_len + 1), count); // Steal edges. - move_edges(left, new_left_len + 1, right, 0, count); + move_to_slice( + left.edge_area_mut(new_left_len + 1..old_left_len + 1), + right.edge_area_mut(..count), + ); + + right.correct_childrens_parent_links(0..new_right_len + 1); } (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} _ => unreachable!(), @@ -1537,36 +1416,43 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { // Move leaf data. { - let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); - let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); - let parent_kv = { - let kv = self.parent.kv_mut(); - (kv.0 as *mut K, kv.1 as *mut V) - }; + // Move the right-most stolen pair to the parent. + let k = right_node.key_area_mut(count - 1).assume_init_read(); + let v = right_node.val_area_mut(count - 1).assume_init_read(); + let (k, v) = self.parent.replace_kv(k, v); // Move parent's key-value pair to the left child. - move_kv(parent_kv, 0, left_kv, old_left_len, 1); + left_node.key_area_mut(old_left_len).write(k); + left_node.val_area_mut(old_left_len).write(v); // Move elements from the right child to the left one. - move_kv(right_kv, 0, left_kv, old_left_len + 1, count - 1); - - // Move the right-most stolen pair to the parent. - move_kv(right_kv, count - 1, parent_kv, 0, 1); + move_to_slice( + right_node.key_area_mut(..count - 1), + left_node.key_area_mut(old_left_len + 1..new_left_len), + ); + move_to_slice( + right_node.val_area_mut(..count - 1), + left_node.val_area_mut(old_left_len + 1..new_left_len), + ); // Fill gap where stolen elements used to be. - ptr::copy(right_kv.0.add(count), right_kv.0, new_right_len); - ptr::copy(right_kv.1.add(count), right_kv.1, new_right_len); + slice_shl(right_node.key_area_mut(..old_right_len), count); + slice_shl(right_node.val_area_mut(..old_right_len), count); } match (left_node.reborrow_mut().force(), right_node.reborrow_mut().force()) { - (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { + (ForceResult::Internal(mut left), ForceResult::Internal(mut right)) => { // Steal edges. - move_edges(right.reborrow(), 0, left, old_left_len + 1, count); + move_to_slice( + right.edge_area_mut(..count), + left.edge_area_mut(old_left_len + 1..new_left_len + 1), + ); // Fill gap where stolen edges used to be. - let right_edges = right.edge_area_mut_at(..).as_mut_ptr(); - ptr::copy(right_edges.add(count), right_edges, new_right_len + 1); - right.correct_childrens_parent_links(0..=new_right_len); + slice_shl(right.edge_area_mut(..old_right_len + 1), count); + + left.correct_childrens_parent_links(old_left_len + 1..new_left_len + 1); + right.correct_childrens_parent_links(0..new_right_len + 1); } (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} _ => unreachable!(), @@ -1575,35 +1461,6 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } } -unsafe fn move_kv( - source: (*mut K, *mut V), - source_offset: usize, - dest: (*mut K, *mut V), - dest_offset: usize, - count: usize, -) { - unsafe { - ptr::copy_nonoverlapping(source.0.add(source_offset), dest.0.add(dest_offset), count); - ptr::copy_nonoverlapping(source.1.add(source_offset), dest.1.add(dest_offset), count); - } -} - -// Source and destination must have the same height. -unsafe fn move_edges<'a, K: 'a, V: 'a>( - source: NodeRef, K, V, marker::Internal>, - source_offset: usize, - mut dest: NodeRef, K, V, marker::Internal>, - dest_offset: usize, - count: usize, -) { - unsafe { - let source_ptr = source.edge_area().as_ptr(); - let dest_ptr = dest.edge_area_mut_at(dest_offset..).as_mut_ptr(); - ptr::copy_nonoverlapping(source_ptr.add(source_offset), dest_ptr, count); - dest.correct_childrens_parent_links(dest_offset..dest_offset + count); - } -} - impl NodeRef { /// Removes any static information asserting that this node is a `Leaf` node. pub fn forget_type(self) -> NodeRef { @@ -1681,26 +1538,33 @@ impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, ma unsafe { let new_left_len = self.idx; let mut left_node = self.reborrow_mut().into_node(); + let old_left_len = left_node.len(); - let new_right_len = left_node.len() - new_left_len; + let new_right_len = old_left_len - new_left_len; let mut right_node = right.reborrow_mut(); assert!(right_node.len() == 0); assert!(left_node.height == right_node.height); if new_right_len > 0 { - let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); - let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); - - move_kv(left_kv, new_left_len, right_kv, 0, new_right_len); - *left_node.len_mut() = new_left_len as u16; *right_node.len_mut() = new_right_len as u16; + move_to_slice( + left_node.key_area_mut(new_left_len..old_left_len), + right_node.key_area_mut(..new_right_len), + ); + move_to_slice( + left_node.val_area_mut(new_left_len..old_left_len), + right_node.val_area_mut(..new_right_len), + ); match (left_node.force(), right_node.force()) { - (ForceResult::Internal(left), ForceResult::Internal(right)) => { - let left = left.reborrow(); - move_edges(left, new_left_len + 1, right, 1, new_right_len); + (ForceResult::Internal(mut left), ForceResult::Internal(mut right)) => { + move_to_slice( + left.edge_area_mut(new_left_len + 1..old_left_len + 1), + right.edge_area_mut(1..new_right_len + 1), + ); + right.correct_childrens_parent_links(1..new_right_len + 1); } (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} _ => unreachable!(), @@ -1750,10 +1614,27 @@ pub mod marker { pub enum LeafOrInternal {} pub enum Owned {} + pub enum Dying {} pub struct Immut<'a>(PhantomData<&'a ()>); pub struct Mut<'a>(PhantomData<&'a mut ()>); pub struct ValMut<'a>(PhantomData<&'a mut ()>); + pub trait BorrowType { + // Whether node references of this borrow type allow traversing + // to other nodes in the tree. + const PERMITS_TRAVERSAL: bool = true; + } + impl BorrowType for Owned { + // Traversal isn't needede, it happens using the result of `borrow_mut`. + // By disabling traversal, and only creating new references to roots, + // we know that every reference of the `Owned` type is to a root node. + const PERMITS_TRAVERSAL: bool = false; + } + impl BorrowType for Dying {} + impl<'a> BorrowType for Immut<'a> {} + impl<'a> BorrowType for Mut<'a> {} + impl<'a> BorrowType for ValMut<'a> {} + pub enum KV {} pub enum Edge {} } @@ -1790,5 +1671,37 @@ unsafe fn slice_remove(slice: &mut [MaybeUninit], idx: usize) -> T { } } +/// Shifts the elements in a slice `distance` positions to the left. +/// +/// # Safety +/// The slice has at least `distance` elements. +unsafe fn slice_shl(slice: &mut [MaybeUninit], distance: usize) { + unsafe { + let slice_ptr = slice.as_mut_ptr(); + ptr::copy(slice_ptr.add(distance), slice_ptr, slice.len() - distance); + } +} + +/// Shifts the elements in a slice `distance` positions to the right. +/// +/// # Safety +/// The slice has at least `distance` elements. +unsafe fn slice_shr(slice: &mut [MaybeUninit], distance: usize) { + unsafe { + let slice_ptr = slice.as_mut_ptr(); + ptr::copy(slice_ptr, slice_ptr.add(distance), slice.len() - distance); + } +} + +/// Moves all values from a slice of initialized elements to a slice +/// of uninitialized elements, leaving behind `src` as all uninitialized. +/// Works like `dst.copy_from_slice(src)` but does not require `T` to be `Copy`. +fn move_to_slice(src: &mut [MaybeUninit], dst: &mut [MaybeUninit]) { + assert!(src.len() == dst.len()); + unsafe { + ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len()); + } +} + #[cfg(test)] mod tests; diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index 7fe8ff743c..acb7210ca7 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -29,17 +29,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> navigate::Position::Leaf(leaf) => { let depth = self.height(); let indent = " ".repeat(depth); - result += &format!("\n{}", indent); - if leaf.len() == 0 { - result += "(empty node)"; - } else { - for idx in 0..leaf.len() { - if idx > 0 { - result += ", "; - } - result += &format!("{:?}", unsafe { leaf.key_at(idx) }); - } - } + result += &format!("\n{}{:?}", indent, leaf.keys()); } navigate::Position::Internal(_) => {} navigate::Position::InternalKV(kv) => { @@ -79,10 +69,8 @@ fn test_splitpoint() { #[test] fn test_partial_cmp_eq() { let mut root1 = NodeRef::new_leaf(); - let mut leaf1 = root1.borrow_mut(); - leaf1.push(1, ()); - let mut root1 = root1.forget_type(); - root1.push_internal_level(); + root1.borrow_mut().push(1, ()); + let mut root1 = NodeRef::new_internal(root1.forget_type()).forget_type(); let root2 = Root::new(); root1.reborrow().assert_back_pointers(); root2.reborrow().assert_back_pointers(); @@ -107,8 +95,8 @@ fn test_partial_cmp_eq() { assert_eq!(top_edge_1.partial_cmp(&top_edge_2), None); root1.pop_internal_level(); - unsafe { root1.deallocate_and_ascend() }; - unsafe { root2.deallocate_and_ascend() }; + unsafe { root1.into_dying().deallocate_and_ascend() }; + unsafe { root2.into_dying().deallocate_and_ascend() }; } #[test] diff --git a/library/alloc/src/collections/btree/remove.rs b/library/alloc/src/collections/btree/remove.rs index 7aeb39cbc3..ff842197d1 100644 --- a/library/alloc/src/collections/btree/remove.rs +++ b/library/alloc/src/collections/btree/remove.rs @@ -33,7 +33,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark Ok(Left(left_parent_kv)) => { debug_assert!(left_parent_kv.right_child_len() == MIN_LEN - 1); if left_parent_kv.can_merge() { - left_parent_kv.merge(Some(Right(idx))) + left_parent_kv.merge_tracking_child_edge(Right(idx)) } else { debug_assert!(left_parent_kv.left_child_len() > MIN_LEN); left_parent_kv.steal_left(idx) @@ -42,7 +42,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark Ok(Right(right_parent_kv)) => { debug_assert!(right_parent_kv.left_child_len() == MIN_LEN - 1); if right_parent_kv.can_merge() { - right_parent_kv.merge(Some(Left(idx))) + right_parent_kv.merge_tracking_child_edge(Left(idx)) } else { debug_assert!(right_parent_kv.right_child_len() > MIN_LEN); right_parent_kv.steal_right(idx) @@ -121,27 +121,25 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { self, ) -> Option, K, V, marker::Internal>> { match self.forget_type().choose_parent_kv() { - Ok(Left(left_parent_kv)) => { + Ok(Left(mut left_parent_kv)) => { debug_assert_eq!(left_parent_kv.right_child_len(), MIN_LEN - 1); if left_parent_kv.can_merge() { - let pos = left_parent_kv.merge(None); - let parent_edge = unsafe { unwrap_unchecked(pos.into_node().ascend().ok()) }; - Some(parent_edge.into_node()) + let parent = left_parent_kv.merge_tracking_parent(); + Some(parent) } else { debug_assert!(left_parent_kv.left_child_len() > MIN_LEN); - left_parent_kv.steal_left(0); + left_parent_kv.bulk_steal_left(1); None } } - Ok(Right(right_parent_kv)) => { + Ok(Right(mut right_parent_kv)) => { debug_assert_eq!(right_parent_kv.left_child_len(), MIN_LEN - 1); if right_parent_kv.can_merge() { - let pos = right_parent_kv.merge(None); - let parent_edge = unsafe { unwrap_unchecked(pos.into_node().ascend().ok()) }; - Some(parent_edge.into_node()) + let parent = right_parent_kv.merge_tracking_parent(); + Some(parent) } else { debug_assert!(right_parent_kv.right_child_len() > MIN_LEN); - right_parent_kv.steal_right(0); + right_parent_kv.bulk_steal_right(1); None } } diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index ed7f95fe63..f87444b7cd 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -10,80 +10,76 @@ pub enum SearchResult { GoDown(Handle, marker::Edge>), } -/// Looks up a given key in a (sub)tree headed by the given node, recursively. -/// Returns a `Found` with the handle of the matching KV, if any. Otherwise, -/// returns a `GoDown` with the handle of the possible leaf edge where the key -/// belongs. -/// -/// The result is meaningful only if the tree is ordered by key, like the tree -/// in a `BTreeMap` is. -pub fn search_tree( - mut node: NodeRef, - key: &Q, -) -> SearchResult -where - Q: Ord, - K: Borrow, -{ - loop { - match search_node(node, key) { - Found(handle) => return Found(handle), - GoDown(handle) => match handle.force() { - Leaf(leaf) => return GoDown(leaf), - Internal(internal) => { - node = internal.descend(); - continue; - } - }, +pub enum IndexResult { + KV(usize), + Edge(usize), +} + +impl NodeRef { + /// Looks up a given key in a (sub)tree headed by the node, recursively. + /// Returns a `Found` with the handle of the matching KV, if any. Otherwise, + /// returns a `GoDown` with the handle of the leaf edge where the key belongs. + /// + /// The result is meaningful only if the tree is ordered by key, like the tree + /// in a `BTreeMap` is. + pub fn search_tree( + mut self, + key: &Q, + ) -> SearchResult + where + Q: Ord, + K: Borrow, + { + loop { + self = match self.search_node(key) { + Found(handle) => return Found(handle), + GoDown(handle) => match handle.force() { + Leaf(leaf) => return GoDown(leaf), + Internal(internal) => internal.descend(), + }, + } } } } -/// Looks up a given key in a given node, without recursion. -/// Returns a `Found` with the handle of the matching KV, if any. Otherwise, -/// returns a `GoDown` with the handle of the edge where the key might be found -/// (if the node is internal) or where the key can be inserted. -/// -/// The result is meaningful only if the tree is ordered by key, like the tree -/// in a `BTreeMap` is. -pub fn search_node( - node: NodeRef, - key: &Q, -) -> SearchResult -where - Q: Ord, - K: Borrow, -{ - match search_linear(&node, key) { - (idx, true) => Found(unsafe { Handle::new_kv(node, idx) }), - (idx, false) => GoDown(unsafe { Handle::new_edge(node, idx) }), +impl NodeRef { + /// Looks up a given key in the node, without recursion. + /// Returns a `Found` with the handle of the matching KV, if any. Otherwise, + /// returns a `GoDown` with the handle of the edge where the key might be found + /// (if the node is internal) or where the key can be inserted. + /// + /// The result is meaningful only if the tree is ordered by key, like the tree + /// in a `BTreeMap` is. + pub fn search_node(self, key: &Q) -> SearchResult + where + Q: Ord, + K: Borrow, + { + match self.find_index(key) { + IndexResult::KV(idx) => Found(unsafe { Handle::new_kv(self, idx) }), + IndexResult::Edge(idx) => GoDown(unsafe { Handle::new_edge(self, idx) }), + } } -} -/// Returns either the KV index in the node at which the key (or an equivalent) -/// exists and `true`, or the edge index where the key belongs and `false`. -/// -/// The result is meaningful only if the tree is ordered by key, like the tree -/// in a `BTreeMap` is. -fn search_linear( - node: &NodeRef, - key: &Q, -) -> (usize, bool) -where - Q: Ord, - K: Borrow, -{ - // This function is defined over all borrow types (immutable, mutable, owned). - // Using `keys_at()` is fine here even if BorrowType is mutable, as all we return - // is an index -- not a reference. - let len = node.len(); - for i in 0..len { - let k = unsafe { node.reborrow().key_at(i) }; - match key.cmp(k.borrow()) { - Ordering::Greater => {} - Ordering::Equal => return (i, true), - Ordering::Less => return (i, false), + /// Returns either the KV index in the node at which the key (or an equivalent) + /// exists, or the edge index where the key belongs. + /// + /// The result is meaningful only if the tree is ordered by key, like the tree + /// in a `BTreeMap` is. + fn find_index(&self, key: &Q) -> IndexResult + where + Q: Ord, + K: Borrow, + { + let node = self.reborrow(); + let keys = node.keys(); + for (i, k) in keys.iter().enumerate() { + match key.cmp(k.borrow()) { + Ordering::Greater => {} + Ordering::Equal => return IndexResult::KV(i), + Ordering::Less => return IndexResult::Edge(i), + } } + IndexResult::Edge(keys.len()) } - (len, false) } diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index f63c3dd580..be4e50119c 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -22,6 +22,9 @@ use super::Recover; /// It is a logic error for an item to be modified in such a way that the item's ordering relative /// to any other item, as determined by the [`Ord`] trait, changes while it is in the set. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. +/// The behavior resulting from such a logic error is not specified, but will not result in +/// undefined behavior. This could include panics, incorrect results, aborts, memory leaks, and +/// non-termination. /// /// [`Ord`]: core::cmp::Ord /// [`Cell`]: core::cell::Cell @@ -219,7 +222,7 @@ impl fmt::Debug for Union<'_, T> { // and it's a power of two to make that division cheap. const ITER_PERFORMANCE_TIPPING_SIZE_DIFF: usize = 16; -impl BTreeSet { +impl BTreeSet { /// Makes a new, empty `BTreeSet`. /// /// Does not allocate anything on its own. @@ -234,7 +237,10 @@ impl BTreeSet { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] - pub const fn new() -> BTreeSet { + pub const fn new() -> BTreeSet + where + T: Ord, + { BTreeSet { map: BTreeMap::new() } } @@ -264,7 +270,7 @@ impl BTreeSet { pub fn range(&self, range: R) -> Range<'_, T> where K: Ord, - T: Borrow, + T: Borrow + Ord, R: RangeBounds, { Range { iter: self.map.range(range) } @@ -291,7 +297,10 @@ impl BTreeSet { /// assert_eq!(diff, [1]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn difference<'a>(&'a self, other: &'a BTreeSet) -> Difference<'a, T> { + pub fn difference<'a>(&'a self, other: &'a BTreeSet) -> Difference<'a, T> + where + T: Ord, + { let (self_min, self_max) = if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { (self_min, self_max) @@ -349,10 +358,10 @@ impl BTreeSet { /// assert_eq!(sym_diff, [1, 3]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn symmetric_difference<'a>( - &'a self, - other: &'a BTreeSet, - ) -> SymmetricDifference<'a, T> { + pub fn symmetric_difference<'a>(&'a self, other: &'a BTreeSet) -> SymmetricDifference<'a, T> + where + T: Ord, + { SymmetricDifference(MergeIterInner::new(self.iter(), other.iter())) } @@ -377,7 +386,10 @@ impl BTreeSet { /// assert_eq!(intersection, [2]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn intersection<'a>(&'a self, other: &'a BTreeSet) -> Intersection<'a, T> { + pub fn intersection<'a>(&'a self, other: &'a BTreeSet) -> Intersection<'a, T> + where + T: Ord, + { let (self_min, self_max) = if let (Some(self_min), Some(self_max)) = (self.first(), self.last()) { (self_min, self_max) @@ -425,7 +437,10 @@ impl BTreeSet { /// assert_eq!(union, [1, 2]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn union<'a>(&'a self, other: &'a BTreeSet) -> Union<'a, T> { + pub fn union<'a>(&'a self, other: &'a BTreeSet) -> Union<'a, T> + where + T: Ord, + { Union(MergeIterInner::new(self.iter(), other.iter())) } @@ -442,7 +457,10 @@ impl BTreeSet { /// assert!(v.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { + pub fn clear(&mut self) + where + T: Ord, + { self.map.clear() } @@ -464,7 +482,7 @@ impl BTreeSet { #[stable(feature = "rust1", since = "1.0.0")] pub fn contains(&self, value: &Q) -> bool where - T: Borrow, + T: Borrow + Ord, Q: Ord, { self.map.contains_key(value) @@ -488,7 +506,7 @@ impl BTreeSet { #[stable(feature = "set_recovery", since = "1.9.0")] pub fn get(&self, value: &Q) -> Option<&T> where - T: Borrow, + T: Borrow + Ord, Q: Ord, { Recover::get(&self.map, value) @@ -512,7 +530,10 @@ impl BTreeSet { /// assert_eq!(a.is_disjoint(&b), false); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_disjoint(&self, other: &BTreeSet) -> bool { + pub fn is_disjoint(&self, other: &BTreeSet) -> bool + where + T: Ord, + { self.intersection(other).next().is_none() } @@ -534,7 +555,10 @@ impl BTreeSet { /// assert_eq!(set.is_subset(&sup), false); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_subset(&self, other: &BTreeSet) -> bool { + pub fn is_subset(&self, other: &BTreeSet) -> bool + where + T: Ord, + { // Same result as self.difference(other).next().is_none() // but the code below is faster (hugely in some cases). if self.len() > other.len() { @@ -610,7 +634,10 @@ impl BTreeSet { /// assert_eq!(set.is_superset(&sub), true); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_superset(&self, other: &BTreeSet) -> bool { + pub fn is_superset(&self, other: &BTreeSet) -> bool + where + T: Ord, + { other.is_subset(self) } @@ -633,7 +660,10 @@ impl BTreeSet { /// assert_eq!(map.first(), Some(&1)); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn first(&self) -> Option<&T> { + pub fn first(&self) -> Option<&T> + where + T: Ord, + { self.map.first_key_value().map(|(k, _)| k) } @@ -656,7 +686,10 @@ impl BTreeSet { /// assert_eq!(map.last(), Some(&2)); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn last(&self) -> Option<&T> { + pub fn last(&self) -> Option<&T> + where + T: Ord, + { self.map.last_key_value().map(|(k, _)| k) } @@ -678,7 +711,10 @@ impl BTreeSet { /// assert!(set.is_empty()); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn pop_first(&mut self) -> Option { + pub fn pop_first(&mut self) -> Option + where + T: Ord, + { self.map.pop_first().map(|kv| kv.0) } @@ -700,7 +736,10 @@ impl BTreeSet { /// assert!(set.is_empty()); /// ``` #[unstable(feature = "map_first_last", issue = "62924")] - pub fn pop_last(&mut self) -> Option { + pub fn pop_last(&mut self) -> Option + where + T: Ord, + { self.map.pop_last().map(|kv| kv.0) } @@ -725,7 +764,10 @@ impl BTreeSet { /// assert_eq!(set.len(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: T) -> bool { + pub fn insert(&mut self, value: T) -> bool + where + T: Ord, + { self.map.insert(value, ()).is_none() } @@ -745,7 +787,10 @@ impl BTreeSet { /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); /// ``` #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn replace(&mut self, value: T) -> Option { + pub fn replace(&mut self, value: T) -> Option + where + T: Ord, + { Recover::replace(&mut self.map, value) } @@ -767,10 +812,11 @@ impl BTreeSet { /// assert_eq!(set.remove(&2), true); /// assert_eq!(set.remove(&2), false); /// ``` + #[doc(alias = "delete")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(&mut self, value: &Q) -> bool where - T: Borrow, + T: Borrow + Ord, Q: Ord, { self.map.remove(value).is_some() @@ -794,7 +840,7 @@ impl BTreeSet { #[stable(feature = "set_recovery", since = "1.9.0")] pub fn take(&mut self, value: &Q) -> Option where - T: Borrow, + T: Borrow + Ord, Q: Ord, { Recover::take(&mut self.map, value) @@ -819,6 +865,7 @@ impl BTreeSet { #[unstable(feature = "btree_retain", issue = "79025")] pub fn retain(&mut self, mut f: F) where + T: Ord, F: FnMut(&T) -> bool, { self.drain_filter(|v| !f(v)); @@ -853,7 +900,10 @@ impl BTreeSet { /// assert!(a.contains(&5)); /// ``` #[stable(feature = "btree_append", since = "1.11.0")] - pub fn append(&mut self, other: &mut Self) { + pub fn append(&mut self, other: &mut Self) + where + T: Ord, + { self.map.append(&mut other.map); } @@ -889,7 +939,7 @@ impl BTreeSet { #[stable(feature = "btree_split_off", since = "1.11.0")] pub fn split_off(&mut self, key: &Q) -> Self where - T: Borrow, + T: Borrow + Ord, { BTreeSet { map: self.map.split_off(key) } } @@ -924,13 +974,12 @@ impl BTreeSet { #[unstable(feature = "btree_drain_filter", issue = "70530")] pub fn drain_filter<'a, F>(&'a mut self, pred: F) -> DrainFilter<'a, T, F> where + T: Ord, F: 'a + FnMut(&T) -> bool, { DrainFilter { pred, inner: self.map.drain_filter_inner() } } -} -impl BTreeSet { /// Gets an iterator that visits the values in the `BTreeSet` in ascending order. /// /// # Examples @@ -975,6 +1024,7 @@ impl BTreeSet { /// v.insert(1); /// assert_eq!(v.len(), 1); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] pub const fn len(&self) -> usize { diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 4d05bc4ebf..79e469eb0d 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -639,6 +639,26 @@ fn test_send() { } } +#[allow(dead_code)] +fn test_ord_absence() { + fn set(set: BTreeSet) { + set.is_empty(); + set.len(); + set.iter(); + set.into_iter(); + } + + fn set_debug(set: BTreeSet) { + format!("{:?}", set); + format!("{:?}", set.iter()); + format!("{:?}", set.into_iter()); + } + + fn set_clone(mut set: BTreeSet) { + set.clone_from(&set.clone()); + } +} + #[test] fn test_append() { let mut a = BTreeSet::new(); @@ -696,8 +716,10 @@ fn test_first_last() { assert_eq!(a.pop_last(), None); } +// Unlike the function with the same name in map/tests, returns no values. +// Which also means it returns different predetermined pseudo-random keys, +// and the test cases using this function explore slightly different trees. fn rand_data(len: usize) -> Vec { - assert!(len <= 70029); // from that point on numbers repeat let mut rng = DeterministicRng::new(); Vec::from_iter((0..len).map(|_| rng.next())) } diff --git a/library/alloc/src/collections/btree/split.rs b/library/alloc/src/collections/btree/split.rs index 6108c139bb..62c5e3a46d 100644 --- a/library/alloc/src/collections/btree/split.rs +++ b/library/alloc/src/collections/btree/split.rs @@ -1,6 +1,6 @@ use super::map::MIN_LEN; use super::node::{ForceResult::*, Root}; -use super::search::{search_node, SearchResult::*}; +use super::search::SearchResult::*; use core::borrow::Borrow; impl Root { @@ -21,7 +21,7 @@ impl Root { let mut right_node = right_root.borrow_mut(); loop { - let mut split_edge = match search_node(left_node, key) { + let mut split_edge = match left_node.search_node(key) { // key is going to the right tree Found(kv) => kv.left_edge(), GoDown(edge) => edge, @@ -53,6 +53,9 @@ impl Root { } } + /// Stock up or merge away any underfull nodes on the right border of the + /// tree. The other nodes, those that are not the root nor a rightmost edge, + /// must already have at least MIN_LEN elements. fn fix_right_border(&mut self) { self.fix_top(); @@ -63,7 +66,7 @@ impl Root { let mut last_kv = node.last_kv().consider_for_balancing(); if last_kv.can_merge() { - cur_node = last_kv.merge(None).into_node(); + cur_node = last_kv.merge_tracking_child(); } else { let right_len = last_kv.right_child_len(); // `MIN_LEN + 1` to avoid readjust if merge happens on the next level. @@ -72,6 +75,7 @@ impl Root { } cur_node = last_kv.into_right_child(); } + debug_assert!(cur_node.len() > MIN_LEN); } } @@ -89,7 +93,7 @@ impl Root { let mut first_kv = node.first_kv().consider_for_balancing(); if first_kv.can_merge() { - cur_node = first_kv.merge(None).into_node(); + cur_node = first_kv.merge_tracking_child(); } else { let left_len = first_kv.left_child_len(); // `MIN_LEN + 1` to avoid readjust if merge happens on the next level. @@ -98,6 +102,7 @@ impl Root { } cur_node = first_kv.into_left_child(); } + debug_assert!(cur_node.len() > MIN_LEN); } } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 4707f12940..397e774f1a 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -593,6 +593,7 @@ impl LinkedList { /// dl.push_back(3); /// assert_eq!(dl.len(), 3); /// ``` + #[doc(alias = "length")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 9e54c15ea6..eb89946819 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -761,8 +761,7 @@ impl VecDeque { /// The capacity will remain at least as large as both the length /// and the supplied value. /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. + /// If the current capacity is less than the lower limit, this is a no-op. /// /// # Examples /// @@ -780,10 +779,9 @@ impl VecDeque { /// ``` #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] pub fn shrink_to(&mut self, min_capacity: usize) { - assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity"); - - // +1 since the ringbuffer always leaves one space empty - // len + 1 can't overflow for an existing, well-formed ringbuffer. + let min_capacity = cmp::min(min_capacity, self.capacity()); + // We don't have to worry about an overflow as neither `self.len()` nor `self.capacity()` + // can ever be `usize::MAX`. +1 as the ringbuffer always leaves one space empty. let target_cap = cmp::max(cmp::max(min_capacity, self.len()) + 1, MINIMUM_CAPACITY + 1) .next_power_of_two(); @@ -1038,6 +1036,7 @@ impl VecDeque { /// v.push_back(1); /// assert_eq!(v.len(), 1); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { count(self.tail, self.head, self.cap()) @@ -1080,8 +1079,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(deque_range)] - /// /// use std::collections::VecDeque; /// /// let v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); @@ -1093,7 +1090,7 @@ impl VecDeque { /// assert_eq!(all.len(), 3); /// ``` #[inline] - #[unstable(feature = "deque_range", issue = "74217")] + #[stable(feature = "deque_range", since = "1.51.0")] pub fn range(&self, range: R) -> Iter<'_, T> where R: RangeBounds, @@ -1117,8 +1114,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(deque_range)] - /// /// use std::collections::VecDeque; /// /// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); @@ -1134,7 +1129,7 @@ impl VecDeque { /// assert_eq!(v, vec![2, 4, 12]); /// ``` #[inline] - #[unstable(feature = "deque_range", issue = "74217")] + #[stable(feature = "deque_range", since = "1.51.0")] pub fn range_mut(&mut self, range: R) -> IterMut<'_, T> where R: RangeBounds, @@ -1295,7 +1290,7 @@ impl VecDeque { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn front(&self) -> Option<&T> { - if !self.is_empty() { Some(&self[0]) } else { None } + self.get(0) } /// Provides a mutable reference to the front element, or `None` if the @@ -1319,7 +1314,7 @@ impl VecDeque { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn front_mut(&mut self) -> Option<&mut T> { - if !self.is_empty() { Some(&mut self[0]) } else { None } + self.get_mut(0) } /// Provides a reference to the back element, or `None` if the `VecDeque` is @@ -1339,7 +1334,7 @@ impl VecDeque { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn back(&self) -> Option<&T> { - if !self.is_empty() { Some(&self[self.len() - 1]) } else { None } + self.get(self.len().wrapping_sub(1)) } /// Provides a mutable reference to the back element, or `None` if the @@ -1363,8 +1358,7 @@ impl VecDeque { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn back_mut(&mut self) -> Option<&mut T> { - let len = self.len(); - if !self.is_empty() { Some(&mut self[len - 1]) } else { None } + self.get_mut(self.len().wrapping_sub(1)) } /// Removes the first element and returns it, or `None` if the `VecDeque` is @@ -2650,9 +2644,13 @@ impl Ord for VecDeque { impl Hash for VecDeque { fn hash(&self, state: &mut H) { self.len().hash(state); - let (a, b) = self.as_slices(); - Hash::hash_slice(a, state); - Hash::hash_slice(b, state); + // It's not possible to use Hash::hash_slice on slices + // returned by as_slices method as their length can vary + // in otherwise identical deques. + // + // Hasher only guarantees equivalence for the exact same + // set of calls to its methods. + self.iter().for_each(|elem| elem.hash(state)); } } diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index 21f52af056..87e06fa394 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -224,6 +224,21 @@ fn make_contiguous_head_to_end() { assert_eq!((&['A', 'B', 'C'] as &[_], &[] as &[_]), dq.as_slices()); } +#[test] +fn make_contiguous_head_to_end_2() { + // Another test case for #79808, taken from #80293. + + let mut dq = VecDeque::from_iter(0..6); + dq.pop_front(); + dq.pop_front(); + dq.push_back(6); + dq.push_back(7); + dq.push_back(8); + dq.make_contiguous(); + let collected: Vec<_> = dq.iter().copied().collect(); + assert_eq!(dq.as_slices(), (&collected[..], &[] as &[_])); +} + #[test] fn test_remove() { // This test checks that every single combination of tail position, length, and @@ -584,3 +599,43 @@ fn issue_53529() { assert_eq!(*a, 2); } } + +#[test] +fn issue_80303() { + use core::iter; + use core::num::Wrapping; + + // This is a valid, albeit rather bad hash function implementation. + struct SimpleHasher(Wrapping); + + impl Hasher for SimpleHasher { + fn finish(&self) -> u64 { + self.0.0 + } + + fn write(&mut self, bytes: &[u8]) { + // This particular implementation hashes value 24 in addition to bytes. + // Such an implementation is valid as Hasher only guarantees equivalence + // for the exact same set of calls to its methods. + for &v in iter::once(&24).chain(bytes) { + self.0 = Wrapping(31) * self.0 + Wrapping(u64::from(v)); + } + } + } + + fn hash_code(value: impl Hash) -> u64 { + let mut hasher = SimpleHasher(Wrapping(1)); + value.hash(&mut hasher); + hasher.finish() + } + + // This creates two deques for which values returned by as_slices + // method differ. + let vda: VecDeque = (0..10).collect(); + let mut vdb = VecDeque::with_capacity(10); + vdb.extend(5..10); + (0..5).rev().for_each(|elem| vdb.push_front(elem)); + assert_ne!(vda.as_slices(), vdb.as_slices()); + assert_eq!(vda, vdb); + assert_eq!(hash_code(vda), hash_code(vdb)); +} diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index 5ebc4d6c4c..f9424b1d74 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -282,21 +282,22 @@ //! `%`. The actual grammar for the formatting syntax is: //! //! ```text -//! format_string := [ maybe-format ] * -//! maybe-format := '{' '{' | '}' '}' | +//! format_string := text [ maybe_format text ] * +//! maybe_format := '{' '{' | '}' '}' | format //! format := '{' [ argument ] [ ':' format_spec ] '}' //! argument := integer | identifier //! -//! format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][type] +//! format_spec := [[fill]align][sign]['#']['0'][width]['.' precision]type //! fill := character //! align := '<' | '^' | '>' //! sign := '+' | '-' //! width := count //! precision := count | '*' -//! type := identifier | '?' | '' +//! type := '' | '?' | 'x?' | 'X?' | identifier //! count := parameter | integer //! parameter := argument '$' //! ``` +//! In the above grammar, `text` may not contain any `'{'` or `'}'` characters. //! //! # Formatting traits //! diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 3ac34c9ae2..a49979fb7a 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -70,19 +70,19 @@ #![warn(missing_docs)] #![warn(missing_debug_implementations)] #![allow(explicit_outlives_requirements)] -#![allow(incomplete_features)] #![deny(unsafe_op_in_unsafe_fn)] #![feature(rustc_allow_const_fn_unstable)] #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(test))] #![cfg_attr(test, feature(new_uninit))] #![feature(allocator_api)] +#![feature(vec_extend_from_within)] #![feature(array_chunks)] #![feature(array_methods)] -#![feature(array_value_iter)] #![feature(array_windows)] #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] +#![feature(async_stream)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(cfg_sanitize)] @@ -90,8 +90,6 @@ #![feature(coerce_unsized)] #![feature(const_btree_new)] #![feature(const_fn)] -#![feature(const_generics)] -#![feature(const_in_array_repeat_expressions)] #![feature(cow_is_borrowed)] #![feature(const_cow_is_borrowed)] #![feature(dispatch_from_dyn)] @@ -112,16 +110,16 @@ #![feature(never_type)] #![feature(nll)] #![feature(nonnull_slice_from_raw_parts)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![feature(or_patterns)] #![feature(pattern)] #![feature(ptr_internals)] #![feature(range_bounds_assert_len)] -#![feature(raw_ref_op)] #![feature(rustc_attrs)] #![feature(receiver_trait)] +#![cfg_attr(bootstrap, feature(min_const_generics))] #![feature(min_specialization)] +#![feature(set_ptr_value)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] #![feature(staged_api)] @@ -140,6 +138,8 @@ #![feature(try_trait)] #![feature(type_alias_impl_trait)] #![feature(associated_type_bounds)] +#![feature(slice_group_by)] +#![feature(decl_macro)] // Allow testing this library #[cfg(test)] @@ -184,11 +184,6 @@ pub mod task; mod tests; pub mod vec; -#[cfg(not(test))] -mod std { - pub use core::ops; // RangeFull -} - #[doc(hidden)] #[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")] pub mod __export { diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 7d4eff6185..a64a8b32ad 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -35,18 +35,20 @@ /// /// [`Vec`]: crate::vec::Vec #[cfg(not(test))] +#[doc(alias = "alloc")] +#[doc(alias = "malloc")] #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] -#[allow_internal_unstable(box_syntax)] +#[allow_internal_unstable(box_syntax, liballoc_internals)] macro_rules! vec { () => ( - $crate::vec::Vec::new() + $crate::__rust_force_expr!($crate::vec::Vec::new()) ); ($elem:expr; $n:expr) => ( - $crate::vec::from_elem($elem, $n) + $crate::__rust_force_expr!($crate::vec::from_elem($elem, $n)) ); ($($x:expr),+ $(,)?) => ( - <[_]>::into_vec(box [$($x),+]) + $crate::__rust_force_expr!(<[_]>::into_vec(box [$($x),+])) ); } @@ -111,3 +113,13 @@ macro_rules! format { res }} } + +/// Force AST node to an expression to improve diagnostics in pattern position. +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")] +macro_rules! __rust_force_expr { + ($e:expr) => { + $e + }; +} diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 36b7efc33a..56f4ebe57f 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -114,6 +114,19 @@ impl RawVec { } impl RawVec { + // Tiny Vecs are dumb. Skip to: + // - 8 if the element size is 1, because any heap allocators is likely + // to round up a request of less than 8 bytes to at least 8 bytes. + // - 4 if elements are moderate-sized (<= 1 KiB). + // - 1 otherwise, to avoid wasting too much space for very short Vecs. + const MIN_NON_ZERO_CAP: usize = if mem::size_of::() == 1 { + 8 + } else if mem::size_of::() <= 1024 { + 4 + } else { + 1 + }; + /// Like `new`, but parameterized over the choice of allocator for /// the returned `RawVec`. #[rustc_allow_const_fn_unstable(const_fn)] @@ -219,6 +232,7 @@ impl RawVec { /// Gets a raw pointer to the start of the allocation. Note that this is /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. + #[inline] pub fn ptr(&self) -> *mut T { self.ptr.as_ptr() } @@ -399,22 +413,7 @@ impl RawVec { // This guarantees exponential growth. The doubling cannot overflow // because `cap <= isize::MAX` and the type of `cap` is `usize`. let cap = cmp::max(self.cap * 2, required_cap); - - // Tiny Vecs are dumb. Skip to: - // - 8 if the element size is 1, because any heap allocators is likely - // to round up a request of less than 8 bytes to at least 8 bytes. - // - 4 if elements are moderate-sized (<= 1 KiB). - // - 1 otherwise, to avoid wasting too much space for very short Vecs. - // Note that `min_non_zero_cap` is computed statically. - let elem_size = mem::size_of::(); - let min_non_zero_cap = if elem_size == 1 { - 8 - } else if elem_size <= 1024 { - 4 - } else { - 1 - }; - let cap = cmp::max(min_non_zero_cap, cap); + let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap); let new_layout = Layout::array::(cap); diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 73d12f0a5f..f67f5fc533 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -238,6 +238,7 @@ //! [downgrade]: Rc::downgrade //! [upgrade]: Weak::upgrade //! [mutability]: core::cell#introducing-mutability-inside-of-something-immutable +//! [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name #![stable(feature = "rust1", since = "1.0.0")] @@ -262,7 +263,9 @@ use core::pin::Pin; use core::ptr::{self, NonNull}; use core::slice::from_raw_parts_mut; -use crate::alloc::{box_free, handle_alloc_error, AllocError, Allocator, Global, Layout}; +use crate::alloc::{ + box_free, handle_alloc_error, AllocError, Allocator, Global, Layout, WriteCloneIntoRaw, +}; use crate::borrow::{Cow, ToOwned}; use crate::string::String; use crate::vec::Vec; @@ -350,6 +353,26 @@ impl Rc { /// to upgrade the weak reference before this function returns will result /// in a `None` value. However, the weak reference may be cloned freely and /// stored for use at a later time. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_new_cyclic)] + /// #![allow(dead_code)] + /// use std::rc::{Rc, Weak}; + /// + /// struct Gadget { + /// self_weak: Weak, + /// // ... more fields + /// } + /// impl Gadget { + /// pub fn new() -> Rc { + /// Rc::new_cyclic(|self_weak| { + /// Gadget { self_weak: self_weak.clone(), /* ... */ } + /// }) + /// } + /// } + /// ``` #[unstable(feature = "arc_new_cyclic", issue = "75861")] pub fn new_cyclic(data_fn: impl FnOnce(&Weak) -> T) -> Rc { // Construct the inner in the "uninitialized" state with a single @@ -375,7 +398,7 @@ impl Rc { unsafe { let inner = init_ptr.as_ptr(); - ptr::write(&raw mut (*inner).value, data); + ptr::write(ptr::addr_of_mut!((*inner).value), data); let prev_value = (*inner).strong.get(); debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); @@ -453,6 +476,95 @@ impl Rc { } } + /// Constructs a new `Rc`, returning an error if the allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::rc::Rc; + /// + /// let five = Rc::try_new(5); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn try_new(value: T) -> Result, AllocError> { + // There is an implicit weak pointer owned by all the strong + // pointers, which ensures that the weak destructor never frees + // the allocation while the strong destructor is running, even + // if the weak pointer is stored inside the strong one. + Ok(Self::from_inner( + Box::leak(Box::try_new(RcBox { strong: Cell::new(1), weak: Cell::new(1), value })?) + .into(), + )) + } + + /// Constructs a new `Rc` with uninitialized contents, returning an error if the allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// #![feature(get_mut_unchecked)] + /// + /// use std::rc::Rc; + /// + /// let mut five = Rc::::try_new_uninit()?; + /// + /// let five = unsafe { + /// // Deferred initialization: + /// Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + pub fn try_new_uninit() -> Result>, AllocError> { + unsafe { + Ok(Rc::from_ptr(Rc::try_allocate_for_layout( + Layout::new::(), + |layout| Global.allocate(layout), + |mem| mem as *mut RcBox>, + )?)) + } + } + + /// Constructs a new `Rc` with uninitialized contents, with the memory + /// being filled with `0` bytes, returning an error if the allocation fails + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and + /// incorrect usage of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::rc::Rc; + /// + /// let zero = Rc::::try_new_zeroed()?; + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_api", issue = "32838")] + //#[unstable(feature = "new_uninit", issue = "63291")] + pub fn try_new_zeroed() -> Result>, AllocError> { + unsafe { + Ok(Rc::from_ptr(Rc::try_allocate_for_layout( + Layout::new::(), + |layout| Global.allocate_zeroed(layout), + |mem| mem as *mut RcBox>, + )?)) + } + } /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then /// `value` will be pinned in memory and unable to be moved. #[stable(feature = "pin", since = "1.33.0")] @@ -692,7 +804,7 @@ impl Rc { // SAFETY: This cannot go through Deref::deref or Rc::inner because // this is required to retain raw/mut provenance such that e.g. `get_mut` can // write through the pointer after the Rc is recovered through `from_raw`. - unsafe { &raw const (*ptr).value } + unsafe { ptr::addr_of_mut!((*ptr).value) } } /// Constructs an `Rc` from a raw pointer. @@ -737,8 +849,8 @@ impl Rc { let offset = unsafe { data_offset(ptr) }; // Reverse the offset to find the original RcBox. - let fake_ptr = ptr as *mut RcBox; - let rc_ptr = unsafe { set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)) }; + let rc_ptr = + unsafe { (ptr as *mut RcBox).set_ptr_value((ptr as *mut u8).offset(-offset)) }; unsafe { Self::from_ptr(rc_ptr) } } @@ -758,7 +870,7 @@ impl Rc { pub fn downgrade(this: &Self) -> Weak { this.inner().inc_weak(); // Make sure we do not create a dangling Weak - debug_assert!(!is_dangling(this.ptr)); + debug_assert!(!is_dangling(this.ptr.as_ptr())); Weak { ptr: this.ptr } } @@ -947,18 +1059,26 @@ impl Rc { #[stable(feature = "rc_unique", since = "1.4.0")] pub fn make_mut(this: &mut Self) -> &mut T { if Rc::strong_count(this) != 1 { - // Gotta clone the data, there are other Rcs - *this = Rc::new((**this).clone()) + // Gotta clone the data, there are other Rcs. + // Pre-allocate memory to allow writing the cloned value directly. + let mut rc = Self::new_uninit(); + unsafe { + let data = Rc::get_mut_unchecked(&mut rc); + (**this).write_clone_into_raw(data.as_mut_ptr()); + *this = rc.assume_init(); + } } else if Rc::weak_count(this) != 0 { // Can just steal the data, all that's left is Weaks + let mut rc = Self::new_uninit(); unsafe { - let mut swap = Rc::new(ptr::read(&this.ptr.as_ref().value)); - mem::swap(this, &mut swap); - swap.inner().dec_strong(); + let data = Rc::get_mut_unchecked(&mut rc); + data.as_mut_ptr().copy_from_nonoverlapping(&**this, 1); + + this.inner().dec_strong(); // Remove implicit strong-weak ref (no need to craft a fake // Weak here -- we know other Weaks can clean up for us) - swap.inner().dec_weak(); - forget(swap); + this.inner().dec_weak(); + ptr::write(this, rc.assume_init()); } } // This unsafety is ok because we're guaranteed that the pointer @@ -1018,9 +1138,32 @@ impl Rc { // `&*(ptr as *const RcBox)`, but this created a misaligned // reference (see #54908). let layout = Layout::new::>().extend(value_layout).unwrap().0.pad_to_align(); + unsafe { + Rc::try_allocate_for_layout(value_layout, allocate, mem_to_rcbox) + .unwrap_or_else(|_| handle_alloc_error(layout)) + } + } + + /// Allocates an `RcBox` with sufficient space for + /// a possibly-unsized inner value where the value has the layout provided, + /// returning an error if allocation fails. + /// + /// The function `mem_to_rcbox` is called with the data pointer + /// and must return back a (potentially fat)-pointer for the `RcBox`. + #[inline] + unsafe fn try_allocate_for_layout( + value_layout: Layout, + allocate: impl FnOnce(Layout) -> Result, AllocError>, + mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox, + ) -> Result<*mut RcBox, AllocError> { + // Calculate layout using the given value layout. + // Previously, layout was calculated on the expression + // `&*(ptr as *const RcBox)`, but this created a misaligned + // reference (see #54908). + let layout = Layout::new::>().extend(value_layout).unwrap().0.pad_to_align(); // Allocate for the layout. - let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + let ptr = allocate(layout)?; // Initialize the RcBox let inner = mem_to_rcbox(ptr.as_non_null_ptr().as_ptr()); @@ -1031,7 +1174,7 @@ impl Rc { ptr::write(&mut (*inner).weak, Cell::new(1)); } - inner + Ok(inner) } /// Allocates an `RcBox` with sufficient space for an unsized inner value @@ -1041,7 +1184,7 @@ impl Rc { Self::allocate_for_layout( Layout::for_value(&*ptr), |layout| Global.allocate(layout), - |mem| set_data_ptr(ptr as *mut T, mem) as *mut RcBox, + |mem| (ptr as *mut RcBox).set_ptr_value(mem), ) } } @@ -1080,20 +1223,7 @@ impl Rc<[T]> { ) } } -} - -/// Sets the data pointer of a `?Sized` raw pointer. -/// -/// For a slice/trait object, this sets the `data` field and leaves the rest -/// unchanged. For a sized raw pointer, this simply sets the pointer. -unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { - unsafe { - ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); - } - ptr -} -impl Rc<[T]> { /// Copy elements from slice into newly allocated Rc<\[T\]> /// /// Unsafe because the caller must either take ownership or bind `T: Copy` @@ -1737,8 +1867,8 @@ impl Weak { } } -pub(crate) fn is_dangling(ptr: NonNull) -> bool { - let address = ptr.as_ptr() as *mut () as usize; +pub(crate) fn is_dangling(ptr: *mut T) -> bool { + let address = ptr as *mut () as usize; address == usize::MAX } @@ -1749,7 +1879,7 @@ struct WeakInner<'a> { strong: &'a Cell, } -impl Weak { +impl Weak { /// Returns a raw pointer to the object `T` pointed to by this `Weak`. /// /// The pointer is valid only if there are some strong references. The pointer may be dangling, @@ -1779,15 +1909,15 @@ impl Weak { pub fn as_ptr(&self) -> *const T { let ptr: *mut RcBox = NonNull::as_ptr(self.ptr); - // SAFETY: we must offset the pointer manually, and said pointer may be - // a dangling weak (usize::MAX) if T is sized. data_offset is safe to call, - // because we know that a pointer to unsized T was derived from a real - // unsized T, as dangling weaks are only created for sized T. wrapping_offset - // is used so that we can use the same code path for the non-dangling - // unsized case and the potentially dangling sized case. - unsafe { - let offset = data_offset(ptr as *mut T); - set_data_ptr(ptr as *mut T, (ptr as *mut u8).wrapping_offset(offset)) + if is_dangling(ptr) { + // If the pointer is dangling, we return the sentinel directly. This cannot be + // a valid payload address, as the payload is at least as aligned as RcBox (usize). + ptr as *const T + } else { + // SAFETY: if is_dangling returns false, then the pointer is dereferencable. + // The payload may be dropped at this point, and we have to maintain provenance, + // so use raw pointer manipulation. + unsafe { ptr::addr_of_mut!((*ptr).value) } } } @@ -1869,22 +1999,24 @@ impl Weak { /// [`new`]: Weak::new #[stable(feature = "weak_into_raw", since = "1.45.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - // SAFETY: data_offset is safe to call, because this pointer originates from a Weak. // See Weak::as_ptr for context on how the input pointer is derived. - let offset = unsafe { data_offset(ptr) }; - // Reverse the offset to find the original RcBox. - // SAFETY: we use wrapping_offset here because the pointer may be dangling (but only if T: Sized). - let ptr = unsafe { - set_data_ptr(ptr as *mut RcBox, (ptr as *mut u8).wrapping_offset(-offset)) + let ptr = if is_dangling(ptr as *mut T) { + // This is a dangling Weak. + ptr as *mut RcBox + } else { + // Otherwise, we're guaranteed the pointer came from a nondangling Weak. + // SAFETY: data_offset is safe to call, as ptr references a real (potentially dropped) T. + let offset = unsafe { data_offset(ptr) }; + // Thus, we reverse the offset to get the whole RcBox. + // SAFETY: the pointer originated from a Weak, so this offset is safe. + unsafe { (ptr as *mut RcBox).set_ptr_value((ptr as *mut u8).offset(-offset)) } }; // SAFETY: we now have recovered the original Weak pointer, so can create the Weak. Weak { ptr: unsafe { NonNull::new_unchecked(ptr) } } } -} -impl Weak { /// Attempts to upgrade the `Weak` pointer to an [`Rc`], delaying /// dropping of the inner value if successful. /// @@ -1947,7 +2079,7 @@ impl Weak { /// (i.e., when this `Weak` was created by `Weak::new`). #[inline] fn inner(&self) -> Option> { - if is_dangling(self.ptr) { + if is_dangling(self.ptr.as_ptr()) { None } else { // We are careful to *not* create a reference covering the "data" field, as @@ -2042,7 +2174,7 @@ impl Drop for Weak { // the strong pointers have disappeared. if inner.weak() == 0 { unsafe { - Global.deallocate(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); + Global.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); } } } @@ -2202,21 +2334,19 @@ impl AsRef for Rc { #[stable(feature = "pin", since = "1.33.0")] impl Unpin for Rc {} -/// Get the offset within an `RcBox` for -/// a payload of type described by a pointer. +/// Get the offset within an `RcBox` for the payload behind a pointer. /// /// # Safety /// -/// This has the same safety requirements as `align_of_val_raw`. In effect: -/// -/// - This function is safe for any argument if `T` is sized, and -/// - if `T` is unsized, the pointer must have appropriate pointer metadata -/// acquired from the real instance that you are getting this offset for. +/// The pointer must point to (and have valid metadata for) a previously +/// valid instance of T, but the T is allowed to be dropped. unsafe fn data_offset(ptr: *const T) -> isize { - // Align the unsized value to the end of the `RcBox`. - // Because it is ?Sized, it will always be the last field in memory. - // Note: This is a detail of the current implementation of the compiler, - // and is not a guaranteed language detail. Do not rely on it outside of std. + // Align the unsized value to the end of the RcBox. + // Because RcBox is repr(C), it will always be the last field in memory. + // SAFETY: since the only unsized types possible are slices, trait objects, + // and extern types, the input safety requirement is currently enough to + // satisfy the requirements of align_of_val_raw; this is an implementation + // detail of the language that may not be relied upon outside of std. unsafe { data_offset_align(align_of_val_raw(ptr)) } } diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index 2d183a8c88..843a9b07fa 100644 --- a/library/alloc/src/rc/tests.rs +++ b/library/alloc/src/rc/tests.rs @@ -208,6 +208,30 @@ fn into_from_weak_raw() { } } +#[test] +fn test_into_from_weak_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let arc: Rc = Rc::from("foo"); + let weak: Weak = Rc::downgrade(&arc); + + let ptr = Weak::into_raw(weak.clone()); + let weak2 = unsafe { Weak::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert!(weak.ptr_eq(&weak2)); + + let arc: Rc = Rc::new(123); + let weak: Weak = Rc::downgrade(&arc); + + let ptr = Weak::into_raw(weak.clone()); + let weak2 = unsafe { Weak::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert!(weak.ptr_eq(&weak2)); +} + #[test] fn get_mut() { let mut x = Rc::new(3); @@ -294,6 +318,23 @@ fn test_unsized() { assert_eq!(foo, foo.clone()); } +#[test] +fn test_maybe_thin_unsized() { + // If/when custom thin DSTs exist, this test should be updated to use one + use std::ffi::{CStr, CString}; + + let x: Rc = Rc::from(CString::new("swordfish").unwrap().into_boxed_c_str()); + assert_eq!(format!("{:?}", x), "\"swordfish\""); + let y: Weak = Rc::downgrade(&x); + drop(x); + + // At this point, the weak points to a dropped DST + assert!(y.upgrade().is_none()); + // But we still need to be able to get the alloc layout to drop. + // CStr has no drop glue, but custom DSTs might, and need to work. + drop(y); +} + #[test] fn test_from_owned() { let foo = 123; diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 064700fc72..cb015b9493 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -110,6 +110,8 @@ pub use core::slice::{Chunks, Windows}; pub use core::slice::{ChunksExact, ChunksExactMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{ChunksMut, Split, SplitMut}; +#[unstable(feature = "slice_group_by", issue = "80552")] +pub use core::slice::{GroupBy, GroupByMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{Iter, IterMut}; #[stable(feature = "rchunks", since = "1.31.0")] diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 578eca7d89..70e0c7dba5 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -451,8 +451,6 @@ impl str { /// Converts a [`Box`] into a [`String`] without copying or allocating. /// - /// [`Box`]: Box - /// /// # Examples /// /// Basic usage: diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 27b32b6950..3218b3535c 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -403,6 +403,8 @@ impl String { /// s.push('a'); /// ``` #[inline] + #[doc(alias = "alloc")] + #[doc(alias = "malloc")] #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(capacity: usize) -> String { String { vec: Vec::with_capacity(capacity) } @@ -970,7 +972,7 @@ impl String { self.vec.try_reserve(additional) } - /// Tries to reserves the minimum capacity for exactly `additional` more elements to + /// Tries to reserve the minimum capacity for exactly `additional` more elements to /// be inserted in the given `String`. After calling `reserve_exact`, /// capacity will be greater than or equal to `self.len() + additional`. /// Does nothing if the capacity is already sufficient. @@ -1034,8 +1036,7 @@ impl String { /// The capacity will remain at least as large as both the length /// and the supplied value. /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. + /// If the current capacity is less than the lower limit, this is a no-op. /// /// # Examples /// @@ -1388,6 +1389,7 @@ impl String { /// assert_eq!(fancy_f.len(), 4); /// assert_eq!(fancy_f.chars().count(), 3); /// ``` + #[doc(alias = "length")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { @@ -1552,18 +1554,25 @@ impl String { // Replace_range does not have the memory safety issues of a vector Splice. // of the vector version. The data is just plain bytes. - match range.start_bound() { + // WARNING: Inlining this variable would be unsound (#81138) + let start = range.start_bound(); + match start { Included(&n) => assert!(self.is_char_boundary(n)), Excluded(&n) => assert!(self.is_char_boundary(n + 1)), Unbounded => {} }; - match range.end_bound() { + // WARNING: Inlining this variable would be unsound (#81138) + let end = range.end_bound(); + match end { Included(&n) => assert!(self.is_char_boundary(n + 1)), Excluded(&n) => assert!(self.is_char_boundary(n)), Unbounded => {} }; - unsafe { self.as_mut_vec() }.splice(range, replace_with.bytes()); + // Using `range` again would be unsound (#81138) + // We assume the bounds reported by `range` remain the same, but + // an adversarial implementation could change between calls + unsafe { self.as_mut_vec() }.splice((start, end), replace_with.bytes()); } /// Converts this `String` into a [`Box`]`<`[`str`]`>`. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 53ba9c283a..461ca85c03 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -14,7 +14,7 @@ use core::hint; use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unpin, Unsize}; -use core::mem::{self, align_of_val, size_of_val}; +use core::mem::{self, align_of_val_raw, size_of_val}; use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver}; use core::pin::Pin; use core::ptr::{self, NonNull}; @@ -22,7 +22,9 @@ use core::slice::from_raw_parts_mut; use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; -use crate::alloc::{box_free, handle_alloc_error, AllocError, Allocator, Global, Layout}; +use crate::alloc::{ + box_free, handle_alloc_error, AllocError, Allocator, Global, Layout, WriteCloneIntoRaw, +}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::rc::is_dangling; @@ -382,7 +384,7 @@ impl Arc { // reference into a strong reference. unsafe { let inner = init_ptr.as_ptr(); - ptr::write(&raw mut (*inner).data, data); + ptr::write(ptr::addr_of_mut!((*inner).data), data); // The above write to the data field must be visible to any threads which // observe a non-zero strong count. Therefore we need at least "Release" ordering @@ -478,6 +480,97 @@ impl Arc { unsafe { Pin::new_unchecked(Arc::new(data)) } } + /// Constructs a new `Arc`, returning an error if allocation fails. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::sync::Arc; + /// + /// let five = Arc::try_new(5)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn try_new(data: T) -> Result, AllocError> { + // Start the weak pointer count as 1 which is the weak pointer that's + // held by all the strong pointers (kinda), see std/rc.rs for more info + let x: Box<_> = Box::try_new(ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data, + })?; + Ok(Self::from_inner(Box::leak(x).into())) + } + + /// Constructs a new `Arc` with uninitialized contents, returning an error + /// if allocation fails. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit, allocator_api)] + /// #![feature(get_mut_unchecked)] + /// + /// use std::sync::Arc; + /// + /// let mut five = Arc::::try_new_uninit()?; + /// + /// let five = unsafe { + /// // Deferred initialization: + /// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + pub fn try_new_uninit() -> Result>, AllocError> { + unsafe { + Ok(Arc::from_ptr(Arc::try_allocate_for_layout( + Layout::new::(), + |layout| Global.allocate(layout), + |mem| mem as *mut ArcInner>, + )?)) + } + } + + /// Constructs a new `Arc` with uninitialized contents, with the memory + /// being filled with `0` bytes, returning an error if allocation fails. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_uninit, allocator_api)] + /// + /// use std::sync::Arc; + /// + /// let zero = Arc::::try_new_zeroed()?; + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + pub fn try_new_zeroed() -> Result>, AllocError> { + unsafe { + Ok(Arc::from_ptr(Arc::try_allocate_for_layout( + Layout::new::(), + |layout| Global.allocate_zeroed(layout), + |mem| mem as *mut ArcInner>, + )?)) + } + } /// Returns the inner value, if the `Arc` has exactly one strong reference. /// /// Otherwise, an [`Err`] is returned with the same `Arc` that was @@ -707,7 +800,7 @@ impl Arc { // SAFETY: This cannot go through Deref::deref or RcBoxPtr::inner because // this is required to retain raw/mut provenance such that e.g. `get_mut` can // write through the pointer after the Rc is recovered through `from_raw`. - unsafe { &raw const (*ptr).data } + unsafe { ptr::addr_of_mut!((*ptr).data) } } /// Constructs an `Arc` from a raw pointer. @@ -753,8 +846,7 @@ impl Arc { let offset = data_offset(ptr); // Reverse the offset to find the original ArcInner. - let fake_ptr = ptr as *mut ArcInner; - let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + let arc_ptr = (ptr as *mut ArcInner).set_ptr_value((ptr as *mut u8).offset(-offset)); Self::from_ptr(arc_ptr) } @@ -795,7 +887,7 @@ impl Arc { match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { Ok(_) => { // Make sure we do not create a dangling Weak - debug_assert!(!is_dangling(this.ptr)); + debug_assert!(!is_dangling(this.ptr.as_ptr())); return Weak { ptr: this.ptr }; } Err(old) => cur = old, @@ -870,15 +962,13 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(arc_mutate_strong_count)] - /// /// use std::sync::Arc; /// /// let five = Arc::new(5); /// /// unsafe { /// let ptr = Arc::into_raw(five); - /// Arc::incr_strong_count(ptr); + /// Arc::increment_strong_count(ptr); /// /// // This assertion is deterministic because we haven't shared /// // the `Arc` between threads. @@ -887,8 +977,8 @@ impl Arc { /// } /// ``` #[inline] - #[unstable(feature = "arc_mutate_strong_count", issue = "71983")] - pub unsafe fn incr_strong_count(ptr: *const T) { + #[stable(feature = "arc_mutate_strong_count", since = "1.51.0")] + pub unsafe fn increment_strong_count(ptr: *const T) { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop let arc = unsafe { mem::ManuallyDrop::new(Arc::::from_raw(ptr)) }; // Now increase refcount, but don't drop new refcount either @@ -909,27 +999,25 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(arc_mutate_strong_count)] - /// /// use std::sync::Arc; /// /// let five = Arc::new(5); /// /// unsafe { /// let ptr = Arc::into_raw(five); - /// Arc::incr_strong_count(ptr); + /// Arc::increment_strong_count(ptr); /// /// // Those assertions are deterministic because we haven't shared /// // the `Arc` between threads. /// let five = Arc::from_raw(ptr); /// assert_eq!(2, Arc::strong_count(&five)); - /// Arc::decr_strong_count(ptr); + /// Arc::decrement_strong_count(ptr); /// assert_eq!(1, Arc::strong_count(&five)); /// } /// ``` #[inline] - #[unstable(feature = "arc_mutate_strong_count", issue = "71983")] - pub unsafe fn decr_strong_count(ptr: *const T) { + #[stable(feature = "arc_mutate_strong_count", since = "1.51.0")] + pub unsafe fn decrement_strong_count(ptr: *const T) { unsafe { mem::drop(Arc::from_raw(ptr)) }; } @@ -994,8 +1082,30 @@ impl Arc { // `&*(ptr as *const ArcInner)`, but this created a misaligned // reference (see #54908). let layout = Layout::new::>().extend(value_layout).unwrap().0.pad_to_align(); + unsafe { + Arc::try_allocate_for_layout(value_layout, allocate, mem_to_arcinner) + .unwrap_or_else(|_| handle_alloc_error(layout)) + } + } + + /// Allocates an `ArcInner` with sufficient space for + /// a possibly-unsized inner value where the value has the layout provided, + /// returning an error if allocation fails. + /// + /// The function `mem_to_arcinner` is called with the data pointer + /// and must return back a (potentially fat)-pointer for the `ArcInner`. + unsafe fn try_allocate_for_layout( + value_layout: Layout, + allocate: impl FnOnce(Layout) -> Result, AllocError>, + mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, + ) -> Result<*mut ArcInner, AllocError> { + // Calculate layout using the given value layout. + // Previously, layout was calculated on the expression + // `&*(ptr as *const ArcInner)`, but this created a misaligned + // reference (see #54908). + let layout = Layout::new::>().extend(value_layout).unwrap().0.pad_to_align(); - let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + let ptr = allocate(layout)?; // Initialize the ArcInner let inner = mem_to_arcinner(ptr.as_non_null_ptr().as_ptr()); @@ -1006,7 +1116,7 @@ impl Arc { ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); } - inner + Ok(inner) } /// Allocates an `ArcInner` with sufficient space for an unsized inner value. @@ -1016,7 +1126,7 @@ impl Arc { Self::allocate_for_layout( Layout::for_value(&*ptr), |layout| Global.allocate(layout), - |mem| set_data_ptr(ptr as *mut T, mem) as *mut ArcInner, + |mem| (ptr as *mut ArcInner).set_ptr_value(mem) as *mut ArcInner, ) } } @@ -1055,20 +1165,7 @@ impl Arc<[T]> { ) } } -} -/// Sets the data pointer of a `?Sized` raw pointer. -/// -/// For a slice/trait object, this sets the `data` field and leaves the rest -/// unchanged. For a sized raw pointer, this simply sets the pointer. -unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { - unsafe { - ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); - } - ptr -} - -impl Arc<[T]> { /// Copy elements from slice into newly allocated Arc<\[T\]> /// /// Unsafe because the caller must either take ownership or bind `T: Copy`. @@ -1256,8 +1353,14 @@ impl Arc { // weak count, there's no chance the ArcInner itself could be // deallocated. if this.inner().strong.compare_exchange(1, 0, Acquire, Relaxed).is_err() { - // Another strong pointer exists; clone - *this = Arc::new((**this).clone()); + // Another strong pointer exists, so we must clone. + // Pre-allocate memory to allow writing the cloned value directly. + let mut arc = Self::new_uninit(); + unsafe { + let data = Arc::get_mut_unchecked(&mut arc); + (**this).write_clone_into_raw(data.as_mut_ptr()); + *this = arc.assume_init(); + } } else if this.inner().weak.load(Relaxed) != 1 { // Relaxed suffices in the above because this is fundamentally an // optimization: we are always racing with weak pointers being @@ -1273,17 +1376,14 @@ impl Arc { // Materialize our own implicit weak pointer, so that it can clean // up the ArcInner as needed. - let weak = Weak { ptr: this.ptr }; + let _weak = Weak { ptr: this.ptr }; - // mark the data itself as already deallocated + // Can just steal the data, all that's left is Weaks + let mut arc = Self::new_uninit(); unsafe { - // there is no data race in the implicit write caused by `read` - // here (due to zeroing) because data is no longer accessed by - // other threads (due to there being no more strong refs at this - // point). - let mut swap = Arc::new(ptr::read(&weak.ptr.as_ref().data)); - mem::swap(this, &mut swap); - mem::forget(swap); + let data = Arc::get_mut_unchecked(&mut arc); + data.as_mut_ptr().copy_from_nonoverlapping(&**this, 1); + ptr::write(this, arc.assume_init()); } } else { // We were the sole reference of either kind; bump back up the @@ -1535,7 +1635,7 @@ struct WeakInner<'a> { strong: &'a atomic::AtomicUsize, } -impl Weak { +impl Weak { /// Returns a raw pointer to the object `T` pointed to by this `Weak`. /// /// The pointer is valid only if there are some strong references. The pointer may be dangling, @@ -1565,15 +1665,15 @@ impl Weak { pub fn as_ptr(&self) -> *const T { let ptr: *mut ArcInner = NonNull::as_ptr(self.ptr); - // SAFETY: we must offset the pointer manually, and said pointer may be - // a dangling weak (usize::MAX) if T is sized. data_offset is safe to call, - // because we know that a pointer to unsized T was derived from a real - // unsized T, as dangling weaks are only created for sized T. wrapping_offset - // is used so that we can use the same code path for the non-dangling - // unsized case and the potentially dangling sized case. - unsafe { - let offset = data_offset(ptr as *mut T); - set_data_ptr(ptr as *mut T, (ptr as *mut u8).wrapping_offset(offset)) + if is_dangling(ptr) { + // If the pointer is dangling, we return the sentinel directly. This cannot be + // a valid payload address, as the payload is at least as aligned as ArcInner (usize). + ptr as *const T + } else { + // SAFETY: if is_dangling returns false, then the pointer is dereferencable. + // The payload may be dropped at this point, and we have to maintain provenance, + // so use raw pointer manipulation. + unsafe { ptr::addr_of_mut!((*ptr).data) } } } @@ -1655,18 +1755,22 @@ impl Weak { /// [`forget`]: std::mem::forget #[stable(feature = "weak_into_raw", since = "1.45.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - // SAFETY: data_offset is safe to call, because this pointer originates from a Weak. // See Weak::as_ptr for context on how the input pointer is derived. - let offset = unsafe { data_offset(ptr) }; - // Reverse the offset to find the original ArcInner. - // SAFETY: we use wrapping_offset here because the pointer may be dangling (but only if T: Sized) - let ptr = unsafe { - set_data_ptr(ptr as *mut ArcInner, (ptr as *mut u8).wrapping_offset(-offset)) + let ptr = if is_dangling(ptr as *mut T) { + // This is a dangling Weak. + ptr as *mut ArcInner + } else { + // Otherwise, we're guaranteed the pointer came from a nondangling Weak. + // SAFETY: data_offset is safe to call, as ptr references a real (potentially dropped) T. + let offset = unsafe { data_offset(ptr) }; + // Thus, we reverse the offset to get the whole RcBox. + // SAFETY: the pointer originated from a Weak, so this offset is safe. + unsafe { (ptr as *mut ArcInner).set_ptr_value((ptr as *mut u8).offset(-offset)) } }; // SAFETY: we now have recovered the original Weak pointer, so can create the Weak. - unsafe { Weak { ptr: NonNull::new_unchecked(ptr) } } + Weak { ptr: unsafe { NonNull::new_unchecked(ptr) } } } } @@ -1771,7 +1875,7 @@ impl Weak { /// (i.e., when this `Weak` was created by `Weak::new`). #[inline] fn inner(&self) -> Option> { - if is_dangling(self.ptr) { + if is_dangling(self.ptr.as_ptr()) { None } else { // We are careful to *not* create a reference covering the "data" field, as @@ -1927,7 +2031,7 @@ impl Drop for Weak { if inner.weak.fetch_sub(1, Release) == 1 { acquire!(inner.weak); - unsafe { Global.deallocate(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) } + unsafe { Global.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())) } } } } @@ -2351,22 +2455,20 @@ impl AsRef for Arc { #[stable(feature = "pin", since = "1.33.0")] impl Unpin for Arc {} -/// Get the offset within an `ArcInner` for -/// a payload of type described by a pointer. +/// Get the offset within an `ArcInner` for the payload behind a pointer. /// /// # Safety /// -/// This has the same safety requirements as `align_of_val_raw`. In effect: -/// -/// - This function is safe for any argument if `T` is sized, and -/// - if `T` is unsized, the pointer must have appropriate pointer metadata -/// acquired from the real instance that you are getting this offset for. +/// The pointer must point to (and have valid metadata for) a previously +/// valid instance of T, but the T is allowed to be dropped. unsafe fn data_offset(ptr: *const T) -> isize { - // Align the unsized value to the end of the `ArcInner`. - // Because it is `?Sized`, it will always be the last field in memory. - // Note: This is a detail of the current implementation of the compiler, - // and is not a guaranteed language detail. Do not rely on it outside of std. - unsafe { data_offset_align(align_of_val(&*ptr)) } + // Align the unsized value to the end of the ArcInner. + // Because RcBox is repr(C), it will always be the last field in memory. + // SAFETY: since the only unsized types possible are slices, trait objects, + // and extern types, the input safety requirement is currently enough to + // satisfy the requirements of align_of_val_raw; this is an implementation + // detail of the language that may not be relied upon outside of std. + unsafe { data_offset_align(align_of_val_raw(ptr)) } } #[inline] diff --git a/library/alloc/src/sync/tests.rs b/library/alloc/src/sync/tests.rs index e8e1e66da5..4ccb32fbbf 100644 --- a/library/alloc/src/sync/tests.rs +++ b/library/alloc/src/sync/tests.rs @@ -158,6 +158,30 @@ fn into_from_weak_raw() { } } +#[test] +fn test_into_from_weak_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let arc: Arc = Arc::from("foo"); + let weak: Weak = Arc::downgrade(&arc); + + let ptr = Weak::into_raw(weak.clone()); + let weak2 = unsafe { Weak::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert!(weak.ptr_eq(&weak2)); + + let arc: Arc = Arc::new(123); + let weak: Weak = Arc::downgrade(&arc); + + let ptr = Weak::into_raw(weak.clone()); + let weak2 = unsafe { Weak::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert!(weak.ptr_eq(&weak2)); +} + #[test] fn test_cowarc_clone_make_mut() { let mut cow0 = Arc::new(75); @@ -329,6 +353,23 @@ fn test_unsized() { assert!(y.upgrade().is_none()); } +#[test] +fn test_maybe_thin_unsized() { + // If/when custom thin DSTs exist, this test should be updated to use one + use std::ffi::{CStr, CString}; + + let x: Arc = Arc::from(CString::new("swordfish").unwrap().into_boxed_c_str()); + assert_eq!(format!("{:?}", x), "\"swordfish\""); + let y: Weak = Arc::downgrade(&x); + drop(x); + + // At this point, the weak points to a dropped DST + assert!(y.upgrade().is_none()); + // But we still need to be able to get the alloc layout to drop. + // CStr has no drop glue, but custom DSTs might, and need to work. + drop(y); +} + #[test] fn test_from_owned() { let foo = 123; @@ -370,7 +411,7 @@ fn test_weak_count_locked() { let n = Arc::weak_count(&a2); assert!(n < 2, "bad weak count: {}", n); #[cfg(miri)] // Miri's scheduler does not guarantee liveness, and thus needs this hint. - atomic::spin_loop_hint(); + std::hint::spin_loop(); } t.join().unwrap(); } diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index fcab3fd0ba..ab7611ae07 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -1,4 +1,4 @@ -#![unstable(feature = "wake_trait", issue = "69912")] +#![stable(feature = "wake_trait", since = "1.51.0")] //! Types and Traits for working with asynchronous tasks. use core::mem::ManuallyDrop; use core::task::{RawWaker, RawWakerVTable, Waker}; @@ -16,25 +16,78 @@ use crate::sync::Arc; /// to wake up a task is stored in an [`Arc`]. Some executors (especially /// those for embedded systems) cannot use this API, which is why [`RawWaker`] /// exists as an alternative for those systems. -#[unstable(feature = "wake_trait", issue = "69912")] +/// +/// [arc]: ../../std/sync/struct.Arc.html +/// +/// # Examples +/// +/// A basic `block_on` function that takes a future and runs it to completion on +/// the current thread. +/// +/// **Note:** This example trades correctness for simplicity. In order to prevent +/// deadlocks, production-grade implementations will also need to handle +/// intermediate calls to `thread::unpark` as well as nested invocations. +/// +/// ```rust +/// use std::future::Future; +/// use std::sync::Arc; +/// use std::task::{Context, Poll, Wake}; +/// use std::thread::{self, Thread}; +/// +/// /// A waker that wakes up the current thread when called. +/// struct ThreadWaker(Thread); +/// +/// impl Wake for ThreadWaker { +/// fn wake(self: Arc) { +/// self.0.unpark(); +/// } +/// } +/// +/// /// Run a future to completion on the current thread. +/// fn block_on(fut: impl Future) -> T { +/// // Pin the future so it can be polled. +/// let mut fut = Box::pin(fut); +/// +/// // Create a new context to be passed to the future. +/// let t = thread::current(); +/// let waker = Arc::new(ThreadWaker(t)).into(); +/// let mut cx = Context::from_waker(&waker); +/// +/// // Run the future to completion. +/// loop { +/// match fut.as_mut().poll(&mut cx) { +/// Poll::Ready(res) => return res, +/// Poll::Pending => thread::park(), +/// } +/// } +/// } +/// +/// block_on(async { +/// println!("Hi from inside a future!"); +/// }); +/// ``` +#[stable(feature = "wake_trait", since = "1.51.0")] pub trait Wake { /// Wake this task. - #[unstable(feature = "wake_trait", issue = "69912")] + #[stable(feature = "wake_trait", since = "1.51.0")] fn wake(self: Arc); /// Wake this task without consuming the waker. /// /// If an executor supports a cheaper way to wake without consuming the /// waker, it should override this method. By default, it clones the - /// [`Arc`] and calls `wake` on the clone. - #[unstable(feature = "wake_trait", issue = "69912")] + /// [`Arc`] and calls [`wake`] on the clone. + /// + /// [`wake`]: Wake::wake + #[stable(feature = "wake_trait", since = "1.51.0")] fn wake_by_ref(self: &Arc) { self.clone().wake(); } } -#[allow(rustc::ineffective_unstable_trait_impl)] -#[unstable(feature = "wake_trait", issue = "69912")] +#[cfg_attr(bootstrap, allow(rustc::ineffective_unstable_trait_impl))] +#[cfg_attr(not(bootstrap), allow(ineffective_unstable_trait_impl))] +#[stable(feature = "wake_trait", since = "1.51.0")] impl From> for Waker { fn from(waker: Arc) -> Waker { // SAFETY: This is safe because raw_waker safely constructs @@ -43,8 +96,9 @@ impl From> for Waker { } } -#[allow(rustc::ineffective_unstable_trait_impl)] -#[unstable(feature = "wake_trait", issue = "69912")] +#[cfg_attr(bootstrap, allow(rustc::ineffective_unstable_trait_impl))] +#[cfg_attr(not(bootstrap), allow(ineffective_unstable_trait_impl))] +#[stable(feature = "wake_trait", since = "1.51.0")] impl From> for RawWaker { fn from(waker: Arc) -> RawWaker { raw_waker(waker) @@ -60,7 +114,7 @@ impl From> for RawWaker { fn raw_waker(waker: Arc) -> RawWaker { // Increment the reference count of the arc to clone it. unsafe fn clone_waker(waker: *const ()) -> RawWaker { - unsafe { Arc::incr_strong_count(waker as *const W) }; + unsafe { Arc::increment_strong_count(waker as *const W) }; RawWaker::new( waker as *const (), &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), @@ -81,7 +135,7 @@ fn raw_waker(waker: Arc) -> RawWaker { // Decrement the reference count of the Arc on drop unsafe fn drop_waker(waker: *const ()) { - unsafe { Arc::decr_strong_count(waker as *const W) }; + unsafe { Arc::decrement_strong_count(waker as *const W) }; } RawWaker::new( diff --git a/library/alloc/src/vec/cow.rs b/library/alloc/src/vec/cow.rs new file mode 100644 index 0000000000..73d15d3064 --- /dev/null +++ b/library/alloc/src/vec/cow.rs @@ -0,0 +1,35 @@ +use crate::borrow::Cow; +use core::iter::FromIterator; + +use super::Vec; + +#[stable(feature = "cow_from_vec", since = "1.8.0")] +impl<'a, T: Clone> From<&'a [T]> for Cow<'a, [T]> { + fn from(s: &'a [T]) -> Cow<'a, [T]> { + Cow::Borrowed(s) + } +} + +#[stable(feature = "cow_from_vec", since = "1.8.0")] +impl<'a, T: Clone> From> for Cow<'a, [T]> { + fn from(v: Vec) -> Cow<'a, [T]> { + Cow::Owned(v) + } +} + +#[stable(feature = "cow_from_vec_ref", since = "1.28.0")] +impl<'a, T: Clone> From<&'a Vec> for Cow<'a, [T]> { + fn from(v: &'a Vec) -> Cow<'a, [T]> { + Cow::Borrowed(v.as_slice()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> FromIterator for Cow<'a, [T]> +where + T: Clone, +{ + fn from_iter>(it: I) -> Cow<'a, [T]> { + Cow::Owned(FromIterator::from_iter(it)) + } +} diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs new file mode 100644 index 0000000000..fb32d144f8 --- /dev/null +++ b/library/alloc/src/vec/drain.rs @@ -0,0 +1,155 @@ +use crate::alloc::{Allocator, Global}; +use core::fmt; +use core::iter::{FusedIterator, TrustedLen}; +use core::mem::{self}; +use core::ptr::{self, NonNull}; +use core::slice::{self}; + +use super::Vec; + +/// A draining iterator for `Vec`. +/// +/// This `struct` is created by [`Vec::drain`]. +/// See its documentation for more. +/// +/// # Example +/// +/// ``` +/// let mut v = vec![0, 1, 2]; +/// let iter: std::vec::Drain<_> = v.drain(..); +/// ``` +#[stable(feature = "drain", since = "1.6.0")] +pub struct Drain< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, +> { + /// Index of tail to preserve + pub(super) tail_start: usize, + /// Length of tail + pub(super) tail_len: usize, + /// Current remaining range to remove + pub(super) iter: slice::Iter<'a, T>, + pub(super) vec: NonNull>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Drain<'_, T, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Drain").field(&self.iter.as_slice()).finish() + } +} + +impl<'a, T, A: Allocator> Drain<'a, T, A> { + /// Returns the remaining items of this iterator as a slice. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec!['a', 'b', 'c']; + /// let mut drain = vec.drain(..); + /// assert_eq!(drain.as_slice(), &['a', 'b', 'c']); + /// let _ = drain.next().unwrap(); + /// assert_eq!(drain.as_slice(), &['b', 'c']); + /// ``` + #[stable(feature = "vec_drain_as_slice", since = "1.46.0")] + pub fn as_slice(&self) -> &[T] { + self.iter.as_slice() + } + + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn allocator(&self) -> &A { + unsafe { self.vec.as_ref().allocator() } + } +} + +#[stable(feature = "vec_drain_as_slice", since = "1.46.0")] +impl<'a, T, A: Allocator> AsRef<[T]> for Drain<'a, T, A> { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +unsafe impl Sync for Drain<'_, T, A> {} +#[stable(feature = "drain", since = "1.6.0")] +unsafe impl Send for Drain<'_, T, A> {} + +#[stable(feature = "drain", since = "1.6.0")] +impl Iterator for Drain<'_, T, A> { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(|elt| unsafe { ptr::read(elt as *const _) }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl DoubleEndedIterator for Drain<'_, T, A> { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|elt| unsafe { ptr::read(elt as *const _) }) + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl Drop for Drain<'_, T, A> { + fn drop(&mut self) { + /// Continues dropping the remaining elements in the `Drain`, then moves back the + /// un-`Drain`ed elements to restore the original `Vec`. + struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>); + + impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { + fn drop(&mut self) { + // Continue the same loop we have below. If the loop already finished, this does + // nothing. + self.0.for_each(drop); + + if self.0.tail_len > 0 { + unsafe { + let source_vec = self.0.vec.as_mut(); + // memmove back untouched tail, update to new length + let start = source_vec.len(); + let tail = self.0.tail_start; + if tail != start { + let src = source_vec.as_ptr().add(tail); + let dst = source_vec.as_mut_ptr().add(start); + ptr::copy(src, dst, self.0.tail_len); + } + source_vec.set_len(start + self.0.tail_len); + } + } + } + } + + // exhaust self first + while let Some(item) = self.next() { + let guard = DropGuard(self); + drop(item); + mem::forget(guard); + } + + // Drop a `DropGuard` to move back the non-drained tail of `self`. + DropGuard(self); + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl ExactSizeIterator for Drain<'_, T, A> { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Drain<'_, T, A> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Drain<'_, T, A> {} diff --git a/library/alloc/src/vec/drain_filter.rs b/library/alloc/src/vec/drain_filter.rs new file mode 100644 index 0000000000..3c37c92ae4 --- /dev/null +++ b/library/alloc/src/vec/drain_filter.rs @@ -0,0 +1,143 @@ +use crate::alloc::{Allocator, Global}; +use core::ptr::{self}; +use core::slice::{self}; + +use super::Vec; + +/// An iterator which uses a closure to determine if an element should be removed. +/// +/// This struct is created by [`Vec::drain_filter`]. +/// See its documentation for more. +/// +/// # Example +/// +/// ``` +/// #![feature(drain_filter)] +/// +/// let mut v = vec![0, 1, 2]; +/// let iter: std::vec::DrainFilter<_, _> = v.drain_filter(|x| *x % 2 == 0); +/// ``` +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +#[derive(Debug)] +pub struct DrainFilter< + 'a, + T, + F, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> where + F: FnMut(&mut T) -> bool, +{ + pub(super) vec: &'a mut Vec, + /// The index of the item that will be inspected by the next call to `next`. + pub(super) idx: usize, + /// The number of items that have been drained (removed) thus far. + pub(super) del: usize, + /// The original length of `vec` prior to draining. + pub(super) old_len: usize, + /// The filter test predicate. + pub(super) pred: F, + /// A flag that indicates a panic has occurred in the filter test predicate. + /// This is used as a hint in the drop implementation to prevent consumption + /// of the remainder of the `DrainFilter`. Any unprocessed items will be + /// backshifted in the `vec`, but no further items will be dropped or + /// tested by the filter predicate. + pub(super) panic_flag: bool, +} + +impl DrainFilter<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn allocator(&self) -> &A { + self.vec.allocator() + } +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl Iterator for DrainFilter<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + unsafe { + while self.idx < self.old_len { + let i = self.idx; + let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len); + self.panic_flag = true; + let drained = (self.pred)(&mut v[i]); + self.panic_flag = false; + // Update the index *after* the predicate is called. If the index + // is updated prior and the predicate panics, the element at this + // index would be leaked. + self.idx += 1; + if drained { + self.del += 1; + return Some(ptr::read(&v[i])); + } else if self.del > 0 { + let del = self.del; + let src: *const T = &v[i]; + let dst: *mut T = &mut v[i - del]; + ptr::copy_nonoverlapping(src, dst, 1); + } + } + None + } + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.old_len - self.idx)) + } +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl Drop for DrainFilter<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + fn drop(&mut self) { + struct BackshiftOnDrop<'a, 'b, T, F, A: Allocator> + where + F: FnMut(&mut T) -> bool, + { + drain: &'b mut DrainFilter<'a, T, F, A>, + } + + impl<'a, 'b, T, F, A: Allocator> Drop for BackshiftOnDrop<'a, 'b, T, F, A> + where + F: FnMut(&mut T) -> bool, + { + fn drop(&mut self) { + unsafe { + if self.drain.idx < self.drain.old_len && self.drain.del > 0 { + // This is a pretty messed up state, and there isn't really an + // obviously right thing to do. We don't want to keep trying + // to execute `pred`, so we just backshift all the unprocessed + // elements and tell the vec that they still exist. The backshift + // is required to prevent a double-drop of the last successfully + // drained item prior to a panic in the predicate. + let ptr = self.drain.vec.as_mut_ptr(); + let src = ptr.add(self.drain.idx); + let dst = src.sub(self.drain.del); + let tail_len = self.drain.old_len - self.drain.idx; + src.copy_to(dst, tail_len); + } + self.drain.vec.set_len(self.drain.old_len - self.drain.del); + } + } + } + + let backshift = BackshiftOnDrop { drain: self }; + + // Attempt to consume any remaining elements if the filter predicate + // has not yet panicked. We'll backshift any remaining elements + // whether we've already panicked or if the consumption here panics. + if !backshift.drain.panic_flag { + backshift.drain.for_each(drop); + } + } +} diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs new file mode 100644 index 0000000000..354d25c238 --- /dev/null +++ b/library/alloc/src/vec/in_place_drop.rs @@ -0,0 +1,24 @@ +use core::ptr::{self}; +use core::slice::{self}; + +// A helper struct for in-place iteration that drops the destination slice of iteration, +// i.e. the head. The source slice (the tail) is dropped by IntoIter. +pub(super) struct InPlaceDrop { + pub(super) inner: *mut T, + pub(super) dst: *mut T, +} + +impl InPlaceDrop { + fn len(&self) -> usize { + unsafe { self.dst.offset_from(self.inner) as usize } + } +} + +impl Drop for InPlaceDrop { + #[inline] + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(slice::from_raw_parts_mut(self.inner, self.len())); + } + } +} diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs new file mode 100644 index 0000000000..f131d06bb1 --- /dev/null +++ b/library/alloc/src/vec/into_iter.rs @@ -0,0 +1,283 @@ +use crate::alloc::{Allocator, Global}; +use crate::raw_vec::RawVec; +use core::fmt; +use core::intrinsics::arith_offset; +use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccess}; +use core::marker::PhantomData; +use core::mem::{self}; +use core::ptr::{self, NonNull}; +use core::slice::{self}; + +/// An iterator that moves out of a vector. +/// +/// This `struct` is created by the `into_iter` method on [`Vec`](super::Vec) +/// (provided by the [`IntoIterator`] trait). +/// +/// # Example +/// +/// ``` +/// let v = vec![0, 1, 2]; +/// let iter: std::vec::IntoIter<_> = v.into_iter(); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + pub(super) buf: NonNull, + pub(super) phantom: PhantomData, + pub(super) cap: usize, + pub(super) alloc: A, + pub(super) ptr: *const T, + pub(super) end: *const T, +} + +#[stable(feature = "vec_intoiter_debug", since = "1.13.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoIter").field(&self.as_slice()).finish() + } +} + +impl IntoIter { + /// Returns the remaining items of this iterator as a slice. + /// + /// # Examples + /// + /// ``` + /// let vec = vec!['a', 'b', 'c']; + /// let mut into_iter = vec.into_iter(); + /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + /// let _ = into_iter.next().unwrap(); + /// assert_eq!(into_iter.as_slice(), &['b', 'c']); + /// ``` + #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] + pub fn as_slice(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.ptr, self.len()) } + } + + /// Returns the remaining items of this iterator as a mutable slice. + /// + /// # Examples + /// + /// ``` + /// let vec = vec!['a', 'b', 'c']; + /// let mut into_iter = vec.into_iter(); + /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); + /// into_iter.as_mut_slice()[2] = 'z'; + /// assert_eq!(into_iter.next().unwrap(), 'a'); + /// assert_eq!(into_iter.next().unwrap(), 'b'); + /// assert_eq!(into_iter.next().unwrap(), 'z'); + /// ``` + #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] + pub fn as_mut_slice(&mut self) -> &mut [T] { + unsafe { &mut *self.as_raw_mut_slice() } + } + + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn allocator(&self) -> &A { + &self.alloc + } + + fn as_raw_mut_slice(&mut self) -> *mut [T] { + ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len()) + } + + pub(super) fn drop_remaining(&mut self) { + unsafe { + ptr::drop_in_place(self.as_mut_slice()); + } + self.ptr = self.end; + } + + /// Relinquishes the backing allocation, equivalent to + /// `ptr::write(&mut self, Vec::new().into_iter())` + pub(super) fn forget_allocation(&mut self) { + self.cap = 0; + self.buf = unsafe { NonNull::new_unchecked(RawVec::NEW.ptr()) }; + self.ptr = self.buf.as_ptr(); + self.end = self.buf.as_ptr(); + } +} + +#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")] +impl AsRef<[T]> for IntoIter { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for IntoIter {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for IntoIter {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + if self.ptr as *const _ == self.end { + None + } else if mem::size_of::() == 0 { + // purposefully don't use 'ptr.offset' because for + // vectors with 0-size elements this would return the + // same pointer. + self.ptr = unsafe { arith_offset(self.ptr as *const i8, 1) as *mut T }; + + // Make up a value of this ZST. + Some(unsafe { mem::zeroed() }) + } else { + let old = self.ptr; + self.ptr = unsafe { self.ptr.offset(1) }; + + Some(unsafe { ptr::read(old) }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let exact = if mem::size_of::() == 0 { + (self.end as usize).wrapping_sub(self.ptr as usize) + } else { + unsafe { self.end.offset_from(self.ptr) as usize } + }; + (exact, Some(exact)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must guarantee that `i` is in bounds of the + // `Vec`, so `i` cannot overflow an `isize`, and the `self.ptr.add(i)` + // is guaranteed to pointer to an element of the `Vec` and + // thus guaranteed to be valid to dereference. + // + // Also note the implementation of `Self: TrustedRandomAccess` requires + // that `T: Copy` so reading elements from the buffer doesn't invalidate + // them for `Drop`. + unsafe { + if mem::size_of::() == 0 { mem::zeroed() } else { ptr::read(self.ptr.add(i)) } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + if self.end == self.ptr { + None + } else if mem::size_of::() == 0 { + // See above for why 'ptr.offset' isn't used + self.end = unsafe { arith_offset(self.end as *const i8, -1) as *mut T }; + + // Make up a value of this ZST. + Some(unsafe { mem::zeroed() }) + } else { + self.end = unsafe { self.end.offset(-1) }; + + Some(unsafe { ptr::read(self.end) }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn is_empty(&self) -> bool { + self.ptr == self.end + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIter {} + +#[doc(hidden)] +#[unstable(issue = "none", feature = "std_internals")] +// T: Copy as approximation for !Drop since get_unchecked does not advance self.ptr +// and thus we can't implement drop-handling +unsafe impl TrustedRandomAccess for IntoIter +where + T: Copy, +{ + fn may_have_side_effect() -> bool { + false + } +} + +#[stable(feature = "vec_into_iter_clone", since = "1.8.0")] +impl Clone for IntoIter { + #[cfg(not(test))] + fn clone(&self) -> Self { + self.as_slice().to_vec_in(self.alloc.clone()).into_iter() + } + #[cfg(test)] + fn clone(&self) -> Self { + crate::slice::to_vec(self.as_slice(), self.alloc.clone()).into_iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter { + fn drop(&mut self) { + struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter); + + impl Drop for DropGuard<'_, T, A> { + fn drop(&mut self) { + unsafe { + // `IntoIter::alloc` is not used anymore after this + let alloc = ptr::read(&self.0.alloc); + // RawVec handles deallocation + let _ = RawVec::from_raw_parts_in(self.0.buf.as_ptr(), self.0.cap, alloc); + } + } + } + + let guard = DropGuard(self); + // destroy the remaining elements + unsafe { + ptr::drop_in_place(guard.0.as_raw_mut_slice()); + } + // now `guard` will be dropped and do the rest + } +} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for IntoIter {} + +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl SourceIter for IntoIter { + type Source = Self; + + #[inline] + unsafe fn as_inner(&mut self) -> &mut Self::Source { + self + } +} + +// internal helper trait for in-place iteration specialization. +#[rustc_specialization_trait] +pub(crate) trait AsIntoIter { + type Item; + fn as_into_iter(&mut self) -> &mut IntoIter; +} + +impl AsIntoIter for IntoIter { + type Item = T; + + fn as_into_iter(&mut self) -> &mut IntoIter { + self + } +} diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs new file mode 100644 index 0000000000..b5739970b6 --- /dev/null +++ b/library/alloc/src/vec/is_zero.rs @@ -0,0 +1,71 @@ +use crate::boxed::Box; + +#[rustc_specialization_trait] +pub(super) unsafe trait IsZero { + /// Whether this value is zero + fn is_zero(&self) -> bool; +} + +macro_rules! impl_is_zero { + ($t:ty, $is_zero:expr) => { + unsafe impl IsZero for $t { + #[inline] + fn is_zero(&self) -> bool { + $is_zero(*self) + } + } + }; +} + +impl_is_zero!(i16, |x| x == 0); +impl_is_zero!(i32, |x| x == 0); +impl_is_zero!(i64, |x| x == 0); +impl_is_zero!(i128, |x| x == 0); +impl_is_zero!(isize, |x| x == 0); + +impl_is_zero!(u16, |x| x == 0); +impl_is_zero!(u32, |x| x == 0); +impl_is_zero!(u64, |x| x == 0); +impl_is_zero!(u128, |x| x == 0); +impl_is_zero!(usize, |x| x == 0); + +impl_is_zero!(bool, |x| x == false); +impl_is_zero!(char, |x| x == '\0'); + +impl_is_zero!(f32, |x: f32| x.to_bits() == 0); +impl_is_zero!(f64, |x: f64| x.to_bits() == 0); + +unsafe impl IsZero for *const T { + #[inline] + fn is_zero(&self) -> bool { + (*self).is_null() + } +} + +unsafe impl IsZero for *mut T { + #[inline] + fn is_zero(&self) -> bool { + (*self).is_null() + } +} + +// `Option<&T>` and `Option>` are guaranteed to represent `None` as null. +// For fat pointers, the bytes that would be the pointer metadata in the `Some` +// variant are padding in the `None` variant, so ignoring them and +// zero-initializing instead is ok. +// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of +// `SpecFromElem`. + +unsafe impl IsZero for Option<&T> { + #[inline] + fn is_zero(&self) -> bool { + self.is_none() + } +} + +unsafe impl IsZero for Option> { + #[inline] + fn is_zero(&self) -> bool { + self.is_none() + } +} diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec/mod.rs similarity index 65% rename from library/alloc/src/vec.rs rename to library/alloc/src/vec/mod.rs index 2b08f1f362..ede1601fa3 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec/mod.rs @@ -1,4 +1,3 @@ -// ignore-tidy-filelength //! A contiguous growable array type with heap-allocated contents, written //! `Vec`. //! @@ -59,9 +58,7 @@ use core::convert::TryFrom; use core::fmt; use core::hash::{Hash, Hasher}; use core::intrinsics::{arith_offset, assume}; -use core::iter::{ - FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccess, -}; +use core::iter::FromIterator; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::{self, Index, IndexMut, Range, RangeBounds}; @@ -74,6 +71,61 @@ use crate::boxed::Box; use crate::collections::TryReserveError; use crate::raw_vec::RawVec; +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +pub use self::drain_filter::DrainFilter; + +mod drain_filter; + +#[stable(feature = "vec_splice", since = "1.21.0")] +pub use self::splice::Splice; + +mod splice; + +#[stable(feature = "drain", since = "1.6.0")] +pub use self::drain::Drain; + +mod drain; + +mod cow; + +pub(crate) use self::into_iter::AsIntoIter; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::into_iter::IntoIter; + +mod into_iter; + +use self::is_zero::IsZero; + +mod is_zero; + +mod source_iter_marker; + +mod partial_eq; + +use self::spec_from_elem::SpecFromElem; + +mod spec_from_elem; + +use self::set_len_on_drop::SetLenOnDrop; + +mod set_len_on_drop; + +use self::in_place_drop::InPlaceDrop; + +mod in_place_drop; + +use self::spec_from_iter_nested::SpecFromIterNested; + +mod spec_from_iter_nested; + +use self::spec_from_iter::SpecFromIter; + +mod spec_from_iter; + +use self::spec_extend::SpecExtend; + +mod spec_extend; + /// A contiguous growable array type, written `Vec` but pronounced 'vector'. /// /// # Examples @@ -233,6 +285,27 @@ use crate::raw_vec::RawVec; /// you would see if you coerced it to a slice), followed by [`capacity`]` - /// `[`len`] logically uninitialized, contiguous elements. /// +/// A vector containing the elements `'a'` and `'b'` with capacity 4 can be +/// visualized as below. The top part is the `Vec` struct, it contains a +/// pointer to the head of the allocation in the heap, length and capacity. +/// The bottom part is the allocation on the heap, a contiguous memory block. +/// +/// ```text +/// ptr len capacity +/// +--------+--------+--------+ +/// | 0x0123 | 2 | 4 | +/// +--------+--------+--------+ +/// | +/// v +/// Heap +--------+--------+--------+--------+ +/// | 'a' | 'b' | uninit | uninit | +/// +--------+--------+--------+--------+ +/// ``` +/// +/// - **uninit** represents memory that is not initialized, see [`MaybeUninit`]. +/// - Note: the ABI is not stable and `Vec` makes no guarantees about its memory +/// layout (including the order of fields). +/// /// `Vec` will never perform a "small optimization" where elements are actually /// stored on the stack for two reasons: /// @@ -248,7 +321,7 @@ use crate::raw_vec::RawVec; /// ensures no unnecessary allocations or deallocations occur. Emptying a `Vec` /// and then filling it back up to the same [`len`] should incur no calls to /// the allocator. If you wish to free up unused memory, use -/// [`shrink_to_fit`]. +/// [`shrink_to_fit`] or [`shrink_to`]. /// /// [`push`] and [`insert`] will never (re)allocate if the reported capacity is /// sufficient. [`push`] and [`insert`] *will* (re)allocate if @@ -287,12 +360,14 @@ use crate::raw_vec::RawVec; /// [`String`]: crate::string::String /// [`&str`]: type@str /// [`shrink_to_fit`]: Vec::shrink_to_fit +/// [`shrink_to`]: Vec::shrink_to /// [`capacity`]: Vec::capacity /// [`mem::size_of::`]: core::mem::size_of /// [`len`]: Vec::len /// [`push`]: Vec::push /// [`insert`]: Vec::insert /// [`reserve`]: Vec::reserve +/// [`MaybeUninit`]: core::mem::MaybeUninit /// [owned slice]: Box /// [slice]: ../../std/primitive.slice.html /// [`&`]: ../../std/primitive.reference.html @@ -359,6 +434,7 @@ impl Vec { /// assert!(vec.capacity() >= 11); /// ``` #[inline] + #[doc(alias = "malloc")] #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(capacity: usize) -> Self { Self::with_capacity_in(capacity, Global) @@ -692,6 +768,7 @@ impl Vec { /// vec.reserve(10); /// assert!(vec.capacity() >= 11); /// ``` + #[doc(alias = "realloc")] #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { self.buf.reserve(self.len, additional); @@ -717,6 +794,7 @@ impl Vec { /// vec.reserve_exact(10); /// assert!(vec.capacity() >= 11); /// ``` + #[doc(alias = "realloc")] #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve_exact(&mut self, additional: usize) { self.buf.reserve_exact(self.len, additional); @@ -754,6 +832,7 @@ impl Vec { /// } /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); /// ``` + #[doc(alias = "realloc")] #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { self.buf.try_reserve(self.len, additional) @@ -795,6 +874,7 @@ impl Vec { /// } /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); /// ``` + #[doc(alias = "realloc")] #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { self.buf.try_reserve_exact(self.len, additional) @@ -814,6 +894,7 @@ impl Vec { /// vec.shrink_to_fit(); /// assert!(vec.capacity() >= 3); /// ``` + #[doc(alias = "realloc")] #[stable(feature = "rust1", since = "1.0.0")] pub fn shrink_to_fit(&mut self) { // The capacity is never less than the length, and there's nothing to do when @@ -829,10 +910,7 @@ impl Vec { /// The capacity will remain at least as large as both the length /// and the supplied value. /// - /// # Panics - /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. + /// If the current capacity is less than the lower limit, this is a no-op. /// /// # Examples /// @@ -846,9 +924,12 @@ impl Vec { /// vec.shrink_to(0); /// assert!(vec.capacity() >= 3); /// ``` + #[doc(alias = "realloc")] #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] pub fn shrink_to(&mut self, min_capacity: usize) { - self.buf.shrink_to_fit(cmp::max(self.len, min_capacity)); + if self.capacity() > min_capacity { + self.buf.shrink_to_fit(cmp::max(self.len, min_capacity)); + } } /// Converts the vector into [`Box<[T]>`][owned slice]. @@ -938,6 +1019,9 @@ impl Vec { // such that no value will be dropped twice in case `drop_in_place` // were to panic once (if it panics twice, the program aborts). unsafe { + // Note: It's intentional that this is `>` and not `>=`. + // Changing it to `>=` has negative performance + // implications in some cases. See #78884 for more. if len > self.len { return; } @@ -1559,6 +1643,7 @@ impl Vec { /// let a = vec![1, 2, 3]; /// assert_eq!(a.len(), 3); /// ``` + #[doc(alias = "length")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { @@ -1740,11 +1825,27 @@ impl Vec { #[unstable(feature = "vec_spare_capacity", issue = "75017")] #[inline] pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { + self.split_at_spare_mut().1 + } + + #[inline] + fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit]) { + let ptr = self.as_mut_ptr(); + + // Safety: + // - `ptr` is guaranteed to be in bounds for `capacity` elements + // - `len` is guaranteed to less or equal to `capacity` + // - `MaybeUninit` has the same layout as `T` + let spare_ptr = unsafe { ptr.cast::>().add(self.len) }; + + // Safety: + // - `ptr` is guaranteed to be valid for `len` elements + // - `spare_ptr` is offseted from `ptr` by `len`, so it doesn't overlap `initialized` slice unsafe { - slice::from_raw_parts_mut( - self.as_mut_ptr().add(self.len) as *mut MaybeUninit, - self.buf.capacity() - self.len, - ) + let initialized = slice::from_raw_parts_mut(ptr, self.len); + let spare = slice::from_raw_parts_mut(spare_ptr, self.buf.capacity() - self.len); + + (initialized, spare) } } } @@ -1806,6 +1907,39 @@ impl Vec { pub fn extend_from_slice(&mut self, other: &[T]) { self.spec_extend(other.iter()) } + + /// Copies elements from `src` range to the end of the vector. + /// + /// ## Examples + /// + /// ``` + /// #![feature(vec_extend_from_within)] + /// + /// let mut vec = vec![0, 1, 2, 3, 4]; + /// + /// vec.extend_from_within(2..); + /// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4]); + /// + /// vec.extend_from_within(..2); + /// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1]); + /// + /// vec.extend_from_within(4..8); + /// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]); + /// ``` + #[unstable(feature = "vec_extend_from_within", issue = "81656")] + pub fn extend_from_within(&mut self, src: R) + where + R: RangeBounds, + { + let range = src.assert_len(self.len()); + self.reserve(range.len()); + + // SAFETY: + // - `assert_len` guarantees that the given range is valid for indexing self + unsafe { + self.spec_extend_from_within(range); + } + } } // This code generalizes `extend_with_{element,default}`. @@ -1875,35 +2009,6 @@ impl Vec { } } -// Set the length of the vec when the `SetLenOnDrop` value goes out of scope. -// -// The idea is: The length field in SetLenOnDrop is a local variable -// that the optimizer will see does not alias with any stores through the Vec's data -// pointer. This is a workaround for alias analysis issue #32155 -struct SetLenOnDrop<'a> { - len: &'a mut usize, - local_len: usize, -} - -impl<'a> SetLenOnDrop<'a> { - #[inline] - fn new(len: &'a mut usize) -> Self { - SetLenOnDrop { local_len: *len, len } - } - - #[inline] - fn increment_len(&mut self, increment: usize) { - self.local_len += increment; - } -} - -impl Drop for SetLenOnDrop<'_> { - #[inline] - fn drop(&mut self) { - *self.len = self.local_len; - } -} - impl Vec { /// Removes consecutive repeated elements in the vector according to the /// [`PartialEq`] trait implementation. @@ -1926,27 +2031,6 @@ impl Vec { } } -impl Vec { - /// Removes the first instance of `item` from the vector if the item exists. - /// - /// This method will be removed soon. - #[unstable(feature = "vec_remove_item", reason = "recently added", issue = "40062")] - #[rustc_deprecated( - reason = "Removing the first item equal to a needle is already easily possible \ - with iterators and the current Vec methods. Furthermore, having a method for \ - one particular case of removal (linear search, only the first item, no swap remove) \ - but not for others is inconsistent. This method will be removed soon.", - since = "1.46.0" - )] - pub fn remove_item(&mut self, item: &V) -> Option - where - T: PartialEq, - { - let pos = self.iter().position(|x| *x == *item)?; - Some(self.remove(pos)) - } -} - //////////////////////////////////////////////////////////////////////////////// // Internal methods and functions //////////////////////////////////////////////////////////////////////////////// @@ -1963,128 +2047,59 @@ pub fn from_elem_in(elem: T, n: usize, alloc: A) -> Vec< ::from_elem(elem, n, alloc) } -// Specialization trait used for Vec::from_elem -trait SpecFromElem: Sized { - fn from_elem(elem: Self, n: usize, alloc: A) -> Vec; -} - -impl SpecFromElem for T { - default fn from_elem(elem: Self, n: usize, alloc: A) -> Vec { - let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); - v - } -} - -impl SpecFromElem for i8 { - #[inline] - fn from_elem(elem: i8, n: usize, alloc: A) -> Vec { - if elem == 0 { - return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; - } - unsafe { - let mut v = Vec::with_capacity_in(n, alloc); - ptr::write_bytes(v.as_mut_ptr(), elem as u8, n); - v.set_len(n); - v - } - } +trait ExtendFromWithinSpec { + /// Safety: + /// - `src` needs to be valid index + /// - `self.capacity() - self.len()` must be `>= src.len()` + unsafe fn spec_extend_from_within(&mut self, src: Range); } -impl SpecFromElem for u8 { - #[inline] - fn from_elem(elem: u8, n: usize, alloc: A) -> Vec { - if elem == 0 { - return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; - } - unsafe { - let mut v = Vec::with_capacity_in(n, alloc); - ptr::write_bytes(v.as_mut_ptr(), elem, n); - v.set_len(n); - v - } - } -} +impl ExtendFromWithinSpec for Vec { + default unsafe fn spec_extend_from_within(&mut self, src: Range) { + let initialized = { + let (this, spare) = self.split_at_spare_mut(); -impl SpecFromElem for T { - #[inline] - fn from_elem(elem: T, n: usize, alloc: A) -> Vec { - if elem.is_zero() { - return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; - } - let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); - v - } -} + // Safety: + // - caller guaratees that src is a valid index + let to_clone = unsafe { this.get_unchecked(src) }; -#[rustc_specialization_trait] -unsafe trait IsZero { - /// Whether this value is zero - fn is_zero(&self) -> bool; -} + to_clone.iter().cloned().zip(spare.iter_mut()).map(|(e, s)| s.write(e)).count() + }; -macro_rules! impl_is_zero { - ($t:ty, $is_zero:expr) => { - unsafe impl IsZero for $t { - #[inline] - fn is_zero(&self) -> bool { - $is_zero(*self) - } + // Safety: + // - elements were just initialized + unsafe { + let new_len = self.len() + initialized; + self.set_len(new_len); } - }; -} - -impl_is_zero!(i16, |x| x == 0); -impl_is_zero!(i32, |x| x == 0); -impl_is_zero!(i64, |x| x == 0); -impl_is_zero!(i128, |x| x == 0); -impl_is_zero!(isize, |x| x == 0); - -impl_is_zero!(u16, |x| x == 0); -impl_is_zero!(u32, |x| x == 0); -impl_is_zero!(u64, |x| x == 0); -impl_is_zero!(u128, |x| x == 0); -impl_is_zero!(usize, |x| x == 0); - -impl_is_zero!(bool, |x| x == false); -impl_is_zero!(char, |x| x == '\0'); - -impl_is_zero!(f32, |x: f32| x.to_bits() == 0); -impl_is_zero!(f64, |x: f64| x.to_bits() == 0); - -unsafe impl IsZero for *const T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} - -unsafe impl IsZero for *mut T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() } } -// `Option<&T>` and `Option>` are guaranteed to represent `None` as null. -// For fat pointers, the bytes that would be the pointer metadata in the `Some` -// variant are padding in the `None` variant, so ignoring them and -// zero-initializing instead is ok. -// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of -// `SpecFromElem`. - -unsafe impl IsZero for Option<&T> { - #[inline] - fn is_zero(&self) -> bool { - self.is_none() - } -} - -unsafe impl IsZero for Option> { - #[inline] - fn is_zero(&self) -> bool { - self.is_none() +impl ExtendFromWithinSpec for Vec { + unsafe fn spec_extend_from_within(&mut self, src: Range) { + let count = src.len(); + { + let (init, spare) = self.split_at_spare_mut(); + + // Safety: + // - caller guaratees that `src` is a valid index + let source = unsafe { init.get_unchecked(src) }; + + // Safety: + // - Both pointers are created from unique slice references (`&mut [_]`) + // so they are valid and do not overlap. + // - Elements are :Copy so it's OK to to copy them, without doing + // anything with the original values + // - `count` is equal to the len of `source`, so source is valid for + // `count` reads + // - `.reserve(count)` guarantees that `spare.len() >= count` so spare + // is valid for `count` writes + unsafe { ptr::copy_nonoverlapping(source.as_ptr(), spare.as_mut_ptr() as _, count) }; + } + + // Safety: + // - The elements were just initialized by `copy_nonoverlapping` + self.len += count; } } @@ -2262,351 +2277,6 @@ impl Extend for Vec { } } -/// Specialization trait used for Vec::from_iter -/// -/// ## The delegation graph: -/// -/// ```text -/// +-------------+ -/// |FromIterator | -/// +-+-----------+ -/// | -/// v -/// +-+-------------------------------+ +---------------------+ -/// |SpecFromIter +---->+SpecFromIterNested | -/// |where I: | | |where I: | -/// | Iterator (default)----------+ | | Iterator (default) | -/// | vec::IntoIter | | | TrustedLen | -/// | SourceIterMarker---fallback-+ | | | -/// | slice::Iter | | | -/// | Iterator | +---------------------+ -/// +---------------------------------+ -/// ``` -trait SpecFromIter { - fn from_iter(iter: I) -> Self; -} - -/// Another specialization trait for Vec::from_iter -/// necessary to manually prioritize overlapping specializations -/// see [`SpecFromIter`] for details. -trait SpecFromIterNested { - fn from_iter(iter: I) -> Self; -} - -impl SpecFromIterNested for Vec -where - I: Iterator, -{ - default fn from_iter(mut iterator: I) -> Self { - // Unroll the first iteration, as the vector is going to be - // expanded on this iteration in every case when the iterable is not - // empty, but the loop in extend_desugared() is not going to see the - // vector being full in the few subsequent loop iterations. - // So we get better branch prediction. - let mut vector = match iterator.next() { - None => return Vec::new(), - Some(element) => { - let (lower, _) = iterator.size_hint(); - let mut vector = Vec::with_capacity(lower.saturating_add(1)); - unsafe { - ptr::write(vector.as_mut_ptr(), element); - vector.set_len(1); - } - vector - } - }; - // must delegate to spec_extend() since extend() itself delegates - // to spec_from for empty Vecs - as SpecExtend>::spec_extend(&mut vector, iterator); - vector - } -} - -impl SpecFromIterNested for Vec -where - I: TrustedLen, -{ - fn from_iter(iterator: I) -> Self { - let mut vector = match iterator.size_hint() { - (_, Some(upper)) => Vec::with_capacity(upper), - _ => Vec::new(), - }; - // must delegate to spec_extend() since extend() itself delegates - // to spec_from for empty Vecs - vector.spec_extend(iterator); - vector - } -} - -impl SpecFromIter for Vec -where - I: Iterator, -{ - default fn from_iter(iterator: I) -> Self { - SpecFromIterNested::from_iter(iterator) - } -} - -// A helper struct for in-place iteration that drops the destination slice of iteration, -// i.e. the head. The source slice (the tail) is dropped by IntoIter. -struct InPlaceDrop { - inner: *mut T, - dst: *mut T, -} - -impl InPlaceDrop { - fn len(&self) -> usize { - unsafe { self.dst.offset_from(self.inner) as usize } - } -} - -impl Drop for InPlaceDrop { - #[inline] - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(slice::from_raw_parts_mut(self.inner, self.len())); - } - } -} - -impl SpecFromIter> for Vec { - fn from_iter(iterator: IntoIter) -> Self { - // A common case is passing a vector into a function which immediately - // re-collects into a vector. We can short circuit this if the IntoIter - // has not been advanced at all. - // When it has been advanced We can also reuse the memory and move the data to the front. - // But we only do so when the resulting Vec wouldn't have more unused capacity - // than creating it through the generic FromIterator implementation would. That limitation - // is not strictly necessary as Vec's allocation behavior is intentionally unspecified. - // But it is a conservative choice. - let has_advanced = iterator.buf.as_ptr() as *const _ != iterator.ptr; - if !has_advanced || iterator.len() >= iterator.cap / 2 { - unsafe { - let it = ManuallyDrop::new(iterator); - if has_advanced { - ptr::copy(it.ptr, it.buf.as_ptr(), it.len()); - } - return Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap); - } - } - - let mut vec = Vec::new(); - // must delegate to spec_extend() since extend() itself delegates - // to spec_from for empty Vecs - vec.spec_extend(iterator); - vec - } -} - -fn write_in_place_with_drop( - src_end: *const T, -) -> impl FnMut(InPlaceDrop, T) -> Result, !> { - move |mut sink, item| { - unsafe { - // the InPlaceIterable contract cannot be verified precisely here since - // try_fold has an exclusive reference to the source pointer - // all we can do is check if it's still in range - debug_assert!(sink.dst as *const _ <= src_end, "InPlaceIterable contract violation"); - ptr::write(sink.dst, item); - sink.dst = sink.dst.add(1); - } - Ok(sink) - } -} - -/// Specialization marker for collecting an iterator pipeline into a Vec while reusing the -/// source allocation, i.e. executing the pipeline in place. -/// -/// The SourceIter parent trait is necessary for the specializing function to access the allocation -/// which is to be reused. But it is not sufficient for the specialization to be valid. See -/// additional bounds on the impl. -#[rustc_unsafe_specialization_marker] -trait SourceIterMarker: SourceIter {} - -// The std-internal SourceIter/InPlaceIterable traits are only implemented by chains of -// Adapter>> (all owned by core/std). Additional bounds -// on the adapter implementations (beyond `impl Trait for Adapter`) only depend on other -// traits already marked as specialization traits (Copy, TrustedRandomAccess, FusedIterator). -// I.e. the marker does not depend on lifetimes of user-supplied types. Modulo the Copy hole, which -// several other specializations already depend on. -impl SourceIterMarker for T where T: SourceIter + InPlaceIterable {} - -impl SpecFromIter for Vec -where - I: Iterator + SourceIterMarker, -{ - default fn from_iter(mut iterator: I) -> Self { - // Additional requirements which cannot expressed via trait bounds. We rely on const eval - // instead: - // a) no ZSTs as there would be no allocation to reuse and pointer arithmetic would panic - // b) size match as required by Alloc contract - // c) alignments match as required by Alloc contract - if mem::size_of::() == 0 - || mem::size_of::() - != mem::size_of::<<::Source as AsIntoIter>::Item>() - || mem::align_of::() - != mem::align_of::<<::Source as AsIntoIter>::Item>() - { - // fallback to more generic implementations - return SpecFromIterNested::from_iter(iterator); - } - - let (src_buf, src_ptr, dst_buf, dst_end, cap) = unsafe { - let inner = iterator.as_inner().as_into_iter(); - ( - inner.buf.as_ptr(), - inner.ptr, - inner.buf.as_ptr() as *mut T, - inner.end as *const T, - inner.cap, - ) - }; - - // use try-fold since - // - it vectorizes better for some iterator adapters - // - unlike most internal iteration methods, it only takes a &mut self - // - it lets us thread the write pointer through its innards and get it back in the end - let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf }; - let sink = iterator - .try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(dst_end)) - .unwrap(); - // iteration succeeded, don't drop head - let dst = ManuallyDrop::new(sink).dst; - - let src = unsafe { iterator.as_inner().as_into_iter() }; - // check if SourceIter contract was upheld - // caveat: if they weren't we may not even make it to this point - debug_assert_eq!(src_buf, src.buf.as_ptr()); - // check InPlaceIterable contract. This is only possible if the iterator advanced the - // source pointer at all. If it uses unchecked access via TrustedRandomAccess - // then the source pointer will stay in its initial position and we can't use it as reference - if src.ptr != src_ptr { - debug_assert!( - dst as *const _ <= src.ptr, - "InPlaceIterable contract violation, write pointer advanced beyond read pointer" - ); - } - - // drop any remaining values at the tail of the source - src.drop_remaining(); - // but prevent drop of the allocation itself once IntoIter goes out of scope - src.forget_allocation(); - - let vec = unsafe { - let len = dst.offset_from(dst_buf) as usize; - Vec::from_raw_parts(dst_buf, len, cap) - }; - - vec - } -} - -impl<'a, T: 'a, I> SpecFromIter<&'a T, I> for Vec -where - I: Iterator, - T: Clone, -{ - default fn from_iter(iterator: I) -> Self { - SpecFromIter::from_iter(iterator.cloned()) - } -} - -// This utilizes `iterator.as_slice().to_vec()` since spec_extend -// must take more steps to reason about the final capacity + length -// and thus do more work. `to_vec()` directly allocates the correct amount -// and fills it exactly. -impl<'a, T: 'a + Clone> SpecFromIter<&'a T, slice::Iter<'a, T>> for Vec { - #[cfg(not(test))] - fn from_iter(iterator: slice::Iter<'a, T>) -> Self { - iterator.as_slice().to_vec() - } - - // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is - // required for this method definition, is not available. Instead use the - // `slice::to_vec` function which is only available with cfg(test) - // NB see the slice::hack module in slice.rs for more information - #[cfg(test)] - fn from_iter(iterator: slice::Iter<'a, T>) -> Self { - crate::slice::to_vec(iterator.as_slice(), Global) - } -} - -// Specialization trait used for Vec::extend -trait SpecExtend { - fn spec_extend(&mut self, iter: I); -} - -impl SpecExtend for Vec -where - I: Iterator, -{ - default fn spec_extend(&mut self, iter: I) { - self.extend_desugared(iter) - } -} - -impl SpecExtend for Vec -where - I: TrustedLen, -{ - default fn spec_extend(&mut self, iterator: I) { - // This is the case for a TrustedLen iterator. - let (low, high) = iterator.size_hint(); - if let Some(high_value) = high { - debug_assert_eq!( - low, - high_value, - "TrustedLen iterator's size hint is not exact: {:?}", - (low, high) - ); - } - if let Some(additional) = high { - self.reserve(additional); - unsafe { - let mut ptr = self.as_mut_ptr().add(self.len()); - let mut local_len = SetLenOnDrop::new(&mut self.len); - iterator.for_each(move |element| { - ptr::write(ptr, element); - ptr = ptr.offset(1); - // NB can't overflow since we would have had to alloc the address space - local_len.increment_len(1); - }); - } - } else { - self.extend_desugared(iterator) - } - } -} - -impl SpecExtend> for Vec { - fn spec_extend(&mut self, mut iterator: IntoIter) { - unsafe { - self.append_elements(iterator.as_slice() as _); - } - iterator.ptr = iterator.end; - } -} - -impl<'a, T: 'a, I, A: Allocator + 'a> SpecExtend<&'a T, I> for Vec -where - I: Iterator, - T: Clone, -{ - default fn spec_extend(&mut self, iterator: I) { - self.spec_extend(iterator.cloned()) - } -} - -impl<'a, T: 'a, A: Allocator + 'a> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec -where - T: Copy, -{ - fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { - let slice = iterator.as_slice(); - unsafe { self.append_elements(slice) }; - } -} - impl Vec { // leaf method to which various SpecFrom/SpecExtend implementations delegate when // they have no further optimizations to apply @@ -2646,7 +2316,7 @@ impl Vec { /// This is optimal if: /// /// * The tail (elements in the vector after `range`) is empty, - /// * or `replace_with` yields fewer elements than `range`’s length + /// * or `replace_with` yields fewer or equal elements than `range`’s length /// * or the lower bound of its `size_hint()` is exact. /// /// Otherwise, a temporary vector is allocated and the tail is moved twice. @@ -2758,45 +2428,6 @@ impl<'a, T: Copy + 'a, A: Allocator + 'a> Extend<&'a T> for Vec { } } -macro_rules! __impl_slice_eq1 { - ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?, #[$stability:meta]) => { - #[$stability] - impl PartialEq<$rhs> for $lhs - where - T: PartialEq, - $($ty: $bound)? - { - #[inline] - fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } - #[inline] - fn ne(&self, other: &$rhs) -> bool { self[..] != other[..] } - } - } -} - -__impl_slice_eq1! { [A: Allocator] Vec, Vec, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [A: Allocator] Vec, &[U], #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [A: Allocator] Vec, &mut [U], #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [A: Allocator] &[T], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } -__impl_slice_eq1! { [A: Allocator] &mut [T], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } -__impl_slice_eq1! { [A: Allocator] Vec, [U], #[stable(feature = "partialeq_vec_for_slice", since = "1.48.0")] } -__impl_slice_eq1! { [A: Allocator] [T], Vec, #[stable(feature = "partialeq_vec_for_slice", since = "1.48.0")] } -__impl_slice_eq1! { [A: Allocator] Cow<'_, [T]>, Vec where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [] Cow<'_, [T]>, &[U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [] Cow<'_, [T]>, &mut [U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [A: Allocator, const N: usize] Vec, [U; N], #[stable(feature = "rust1", since = "1.0.0")] } -__impl_slice_eq1! { [A: Allocator, const N: usize] Vec, &[U; N], #[stable(feature = "rust1", since = "1.0.0")] } - -// NOTE: some less important impls are omitted to reduce code bloat -// FIXME(Centril): Reconsider this? -//__impl_slice_eq1! { [const N: usize] Vec, &mut [B; N], } -//__impl_slice_eq1! { [const N: usize] [A; N], Vec, } -//__impl_slice_eq1! { [const N: usize] &[A; N], Vec, } -//__impl_slice_eq1! { [const N: usize] &mut [A; N], Vec, } -//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, [B; N], } -//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &[B; N], } -//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &mut [B; N], } - /// Implements comparison of vectors, [lexicographically](core::cmp::Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for Vec { @@ -2996,729 +2627,3 @@ impl TryFrom> for [T; N] { Ok(array) } } - -//////////////////////////////////////////////////////////////////////////////// -// Clone-on-write -//////////////////////////////////////////////////////////////////////////////// - -#[stable(feature = "cow_from_vec", since = "1.8.0")] -impl<'a, T: Clone> From<&'a [T]> for Cow<'a, [T]> { - fn from(s: &'a [T]) -> Cow<'a, [T]> { - Cow::Borrowed(s) - } -} - -#[stable(feature = "cow_from_vec", since = "1.8.0")] -impl<'a, T: Clone> From> for Cow<'a, [T]> { - fn from(v: Vec) -> Cow<'a, [T]> { - Cow::Owned(v) - } -} - -#[stable(feature = "cow_from_vec_ref", since = "1.28.0")] -impl<'a, T: Clone> From<&'a Vec> for Cow<'a, [T]> { - fn from(v: &'a Vec) -> Cow<'a, [T]> { - Cow::Borrowed(v.as_slice()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> FromIterator for Cow<'a, [T]> -where - T: Clone, -{ - fn from_iter>(it: I) -> Cow<'a, [T]> { - Cow::Owned(FromIterator::from_iter(it)) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Iterators -//////////////////////////////////////////////////////////////////////////////// - -/// An iterator that moves out of a vector. -/// -/// This `struct` is created by the `into_iter` method on [`Vec`] (provided -/// by the [`IntoIterator`] trait). -/// -/// # Example -/// -/// ``` -/// let v = vec![0, 1, 2]; -/// let iter: std::vec::IntoIter<_> = v.into_iter(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter< - T, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - buf: NonNull, - phantom: PhantomData, - cap: usize, - alloc: A, - ptr: *const T, - end: *const T, -} - -#[stable(feature = "vec_intoiter_debug", since = "1.13.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("IntoIter").field(&self.as_slice()).finish() - } -} - -impl IntoIter { - /// Returns the remaining items of this iterator as a slice. - /// - /// # Examples - /// - /// ``` - /// let vec = vec!['a', 'b', 'c']; - /// let mut into_iter = vec.into_iter(); - /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); - /// let _ = into_iter.next().unwrap(); - /// assert_eq!(into_iter.as_slice(), &['b', 'c']); - /// ``` - #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] - pub fn as_slice(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.ptr, self.len()) } - } - - /// Returns the remaining items of this iterator as a mutable slice. - /// - /// # Examples - /// - /// ``` - /// let vec = vec!['a', 'b', 'c']; - /// let mut into_iter = vec.into_iter(); - /// assert_eq!(into_iter.as_slice(), &['a', 'b', 'c']); - /// into_iter.as_mut_slice()[2] = 'z'; - /// assert_eq!(into_iter.next().unwrap(), 'a'); - /// assert_eq!(into_iter.next().unwrap(), 'b'); - /// assert_eq!(into_iter.next().unwrap(), 'z'); - /// ``` - #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] - pub fn as_mut_slice(&mut self) -> &mut [T] { - unsafe { &mut *self.as_raw_mut_slice() } - } - - /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn allocator(&self) -> &A { - &self.alloc - } - - fn as_raw_mut_slice(&mut self) -> *mut [T] { - ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len()) - } - - fn drop_remaining(&mut self) { - unsafe { - ptr::drop_in_place(self.as_mut_slice()); - } - self.ptr = self.end; - } - - /// Relinquishes the backing allocation, equivalent to - /// `ptr::write(&mut self, Vec::new().into_iter())` - fn forget_allocation(&mut self) { - self.cap = 0; - self.buf = unsafe { NonNull::new_unchecked(RawVec::NEW.ptr()) }; - self.ptr = self.buf.as_ptr(); - self.end = self.buf.as_ptr(); - } -} - -#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")] -impl AsRef<[T]> for IntoIter { - fn as_ref(&self) -> &[T] { - self.as_slice() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for IntoIter {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for IntoIter {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - if self.ptr as *const _ == self.end { - None - } else if mem::size_of::() == 0 { - // purposefully don't use 'ptr.offset' because for - // vectors with 0-size elements this would return the - // same pointer. - self.ptr = unsafe { arith_offset(self.ptr as *const i8, 1) as *mut T }; - - // Make up a value of this ZST. - Some(unsafe { mem::zeroed() }) - } else { - let old = self.ptr; - self.ptr = unsafe { self.ptr.offset(1) }; - - Some(unsafe { ptr::read(old) }) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let exact = if mem::size_of::() == 0 { - (self.end as usize).wrapping_sub(self.ptr as usize) - } else { - unsafe { self.end.offset_from(self.ptr) as usize } - }; - (exact, Some(exact)) - } - - #[inline] - fn count(self) -> usize { - self.len() - } - - unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item - where - Self: TrustedRandomAccess, - { - // SAFETY: the caller must guarantee that `i` is in bounds of the - // `Vec`, so `i` cannot overflow an `isize`, and the `self.ptr.add(i)` - // is guaranteed to pointer to an element of the `Vec` and - // thus guaranteed to be valid to dereference. - // - // Also note the implementation of `Self: TrustedRandomAccess` requires - // that `T: Copy` so reading elements from the buffer doesn't invalidate - // them for `Drop`. - unsafe { - if mem::size_of::() == 0 { mem::zeroed() } else { ptr::read(self.ptr.add(i)) } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - if self.end == self.ptr { - None - } else if mem::size_of::() == 0 { - // See above for why 'ptr.offset' isn't used - self.end = unsafe { arith_offset(self.end as *const i8, -1) as *mut T }; - - // Make up a value of this ZST. - Some(unsafe { mem::zeroed() }) - } else { - self.end = unsafe { self.end.offset(-1) }; - - Some(unsafe { ptr::read(self.end) }) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn is_empty(&self) -> bool { - self.ptr == self.end - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IntoIter {} - -#[doc(hidden)] -#[unstable(issue = "none", feature = "std_internals")] -// T: Copy as approximation for !Drop since get_unchecked does not advance self.ptr -// and thus we can't implement drop-handling -unsafe impl TrustedRandomAccess for IntoIter -where - T: Copy, -{ - fn may_have_side_effect() -> bool { - false - } -} - -#[stable(feature = "vec_into_iter_clone", since = "1.8.0")] -impl Clone for IntoIter { - #[cfg(not(test))] - fn clone(&self) -> Self { - self.as_slice().to_vec_in(self.alloc.clone()).into_iter() - } - #[cfg(test)] - fn clone(&self) -> Self { - crate::slice::to_vec(self.as_slice(), self.alloc.clone()).into_iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter { - fn drop(&mut self) { - struct DropGuard<'a, T, A: Allocator>(&'a mut IntoIter); - - impl Drop for DropGuard<'_, T, A> { - fn drop(&mut self) { - unsafe { - // `IntoIter::alloc` is not used anymore after this - let alloc = ptr::read(&self.0.alloc); - // RawVec handles deallocation - let _ = RawVec::from_raw_parts_in(self.0.buf.as_ptr(), self.0.cap, alloc); - } - } - } - - let guard = DropGuard(self); - // destroy the remaining elements - unsafe { - ptr::drop_in_place(guard.0.as_raw_mut_slice()); - } - // now `guard` will be dropped and do the rest - } -} - -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl InPlaceIterable for IntoIter {} - -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl SourceIter for IntoIter { - type Source = Self; - - #[inline] - unsafe fn as_inner(&mut self) -> &mut Self::Source { - self - } -} - -// internal helper trait for in-place iteration specialization. -#[rustc_specialization_trait] -pub(crate) trait AsIntoIter { - type Item; - fn as_into_iter(&mut self) -> &mut IntoIter; -} - -impl AsIntoIter for IntoIter { - type Item = T; - - fn as_into_iter(&mut self) -> &mut IntoIter { - self - } -} - -/// A draining iterator for `Vec`. -/// -/// This `struct` is created by [`Vec::drain`]. -/// See its documentation for more. -/// -/// # Example -/// -/// ``` -/// let mut v = vec![0, 1, 2]; -/// let iter: std::vec::Drain<_> = v.drain(..); -/// ``` -#[stable(feature = "drain", since = "1.6.0")] -pub struct Drain< - 'a, - T: 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, -> { - /// Index of tail to preserve - tail_start: usize, - /// Length of tail - tail_len: usize, - /// Current remaining range to remove - iter: slice::Iter<'a, T>, - vec: NonNull>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Drain<'_, T, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Drain").field(&self.iter.as_slice()).finish() - } -} - -impl<'a, T, A: Allocator> Drain<'a, T, A> { - /// Returns the remaining items of this iterator as a slice. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec!['a', 'b', 'c']; - /// let mut drain = vec.drain(..); - /// assert_eq!(drain.as_slice(), &['a', 'b', 'c']); - /// let _ = drain.next().unwrap(); - /// assert_eq!(drain.as_slice(), &['b', 'c']); - /// ``` - #[stable(feature = "vec_drain_as_slice", since = "1.46.0")] - pub fn as_slice(&self) -> &[T] { - self.iter.as_slice() - } - - /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn allocator(&self) -> &A { - unsafe { self.vec.as_ref().allocator() } - } -} - -#[stable(feature = "vec_drain_as_slice", since = "1.46.0")] -impl<'a, T, A: Allocator> AsRef<[T]> for Drain<'a, T, A> { - fn as_ref(&self) -> &[T] { - self.as_slice() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -unsafe impl Sync for Drain<'_, T, A> {} -#[stable(feature = "drain", since = "1.6.0")] -unsafe impl Send for Drain<'_, T, A> {} - -#[stable(feature = "drain", since = "1.6.0")] -impl Iterator for Drain<'_, T, A> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next().map(|elt| unsafe { ptr::read(elt as *const _) }) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl DoubleEndedIterator for Drain<'_, T, A> { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|elt| unsafe { ptr::read(elt as *const _) }) - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl Drop for Drain<'_, T, A> { - fn drop(&mut self) { - /// Continues dropping the remaining elements in the `Drain`, then moves back the - /// un-`Drain`ed elements to restore the original `Vec`. - struct DropGuard<'r, 'a, T, A: Allocator>(&'r mut Drain<'a, T, A>); - - impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { - fn drop(&mut self) { - // Continue the same loop we have below. If the loop already finished, this does - // nothing. - self.0.for_each(drop); - - if self.0.tail_len > 0 { - unsafe { - let source_vec = self.0.vec.as_mut(); - // memmove back untouched tail, update to new length - let start = source_vec.len(); - let tail = self.0.tail_start; - if tail != start { - let src = source_vec.as_ptr().add(tail); - let dst = source_vec.as_mut_ptr().add(start); - ptr::copy(src, dst, self.0.tail_len); - } - source_vec.set_len(start + self.0.tail_len); - } - } - } - } - - // exhaust self first - while let Some(item) = self.next() { - let guard = DropGuard(self); - drop(item); - mem::forget(guard); - } - - // Drop a `DropGuard` to move back the non-drained tail of `self`. - DropGuard(self); - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl ExactSizeIterator for Drain<'_, T, A> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for Drain<'_, T, A> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, T, A> {} - -/// A splicing iterator for `Vec`. -/// -/// This struct is created by [`Vec::splice()`]. -/// See its documentation for more. -/// -/// # Example -/// -/// ``` -/// let mut v = vec![0, 1, 2]; -/// let new = [7, 8]; -/// let iter: std::vec::Splice<_> = v.splice(1.., new.iter().cloned()); -/// ``` -#[derive(Debug)] -#[stable(feature = "vec_splice", since = "1.21.0")] -pub struct Splice< - 'a, - I: Iterator + 'a, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, -> { - drain: Drain<'a, I::Item, A>, - replace_with: I, -} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl Iterator for Splice<'_, I, A> { - type Item = I::Item; - - fn next(&mut self) -> Option { - self.drain.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.drain.size_hint() - } -} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl DoubleEndedIterator for Splice<'_, I, A> { - fn next_back(&mut self) -> Option { - self.drain.next_back() - } -} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl ExactSizeIterator for Splice<'_, I, A> {} - -#[stable(feature = "vec_splice", since = "1.21.0")] -impl Drop for Splice<'_, I, A> { - fn drop(&mut self) { - self.drain.by_ref().for_each(drop); - - unsafe { - if self.drain.tail_len == 0 { - self.drain.vec.as_mut().extend(self.replace_with.by_ref()); - return; - } - - // First fill the range left by drain(). - if !self.drain.fill(&mut self.replace_with) { - return; - } - - // There may be more elements. Use the lower bound as an estimate. - // FIXME: Is the upper bound a better guess? Or something else? - let (lower_bound, _upper_bound) = self.replace_with.size_hint(); - if lower_bound > 0 { - self.drain.move_tail(lower_bound); - if !self.drain.fill(&mut self.replace_with) { - return; - } - } - - // Collect any remaining elements. - // This is a zero-length vector which does not allocate if `lower_bound` was exact. - let mut collected = self.replace_with.by_ref().collect::>().into_iter(); - // Now we have an exact count. - if collected.len() > 0 { - self.drain.move_tail(collected.len()); - let filled = self.drain.fill(&mut collected); - debug_assert!(filled); - debug_assert_eq!(collected.len(), 0); - } - } - // Let `Drain::drop` move the tail back if necessary and restore `vec.len`. - } -} - -/// Private helper methods for `Splice::drop` -impl Drain<'_, T, A> { - /// The range from `self.vec.len` to `self.tail_start` contains elements - /// that have been moved out. - /// Fill that range as much as possible with new elements from the `replace_with` iterator. - /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) - unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { - let vec = unsafe { self.vec.as_mut() }; - let range_start = vec.len; - let range_end = self.tail_start; - let range_slice = unsafe { - slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start) - }; - - for place in range_slice { - if let Some(new_item) = replace_with.next() { - unsafe { ptr::write(place, new_item) }; - vec.len += 1; - } else { - return false; - } - } - true - } - - /// Makes room for inserting more elements before the tail. - unsafe fn move_tail(&mut self, additional: usize) { - let vec = unsafe { self.vec.as_mut() }; - let len = self.tail_start + self.tail_len; - vec.buf.reserve(len, additional); - - let new_tail_start = self.tail_start + additional; - unsafe { - let src = vec.as_ptr().add(self.tail_start); - let dst = vec.as_mut_ptr().add(new_tail_start); - ptr::copy(src, dst, self.tail_len); - } - self.tail_start = new_tail_start; - } -} - -/// An iterator which uses a closure to determine if an element should be removed. -/// -/// This struct is created by [`Vec::drain_filter`]. -/// See its documentation for more. -/// -/// # Example -/// -/// ``` -/// #![feature(drain_filter)] -/// -/// let mut v = vec![0, 1, 2]; -/// let iter: std::vec::DrainFilter<_, _> = v.drain_filter(|x| *x % 2 == 0); -/// ``` -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -#[derive(Debug)] -pub struct DrainFilter< - 'a, - T, - F, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> where - F: FnMut(&mut T) -> bool, -{ - vec: &'a mut Vec, - /// The index of the item that will be inspected by the next call to `next`. - idx: usize, - /// The number of items that have been drained (removed) thus far. - del: usize, - /// The original length of `vec` prior to draining. - old_len: usize, - /// The filter test predicate. - pred: F, - /// A flag that indicates a panic has occurred in the filter test predicate. - /// This is used as a hint in the drop implementation to prevent consumption - /// of the remainder of the `DrainFilter`. Any unprocessed items will be - /// backshifted in the `vec`, but no further items will be dropped or - /// tested by the filter predicate. - panic_flag: bool, -} - -impl DrainFilter<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ - /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn allocator(&self) -> &A { - self.vec.allocator() - } -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl Iterator for DrainFilter<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ - type Item = T; - - fn next(&mut self) -> Option { - unsafe { - while self.idx < self.old_len { - let i = self.idx; - let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len); - self.panic_flag = true; - let drained = (self.pred)(&mut v[i]); - self.panic_flag = false; - // Update the index *after* the predicate is called. If the index - // is updated prior and the predicate panics, the element at this - // index would be leaked. - self.idx += 1; - if drained { - self.del += 1; - return Some(ptr::read(&v[i])); - } else if self.del > 0 { - let del = self.del; - let src: *const T = &v[i]; - let dst: *mut T = &mut v[i - del]; - ptr::copy_nonoverlapping(src, dst, 1); - } - } - None - } - } - - fn size_hint(&self) -> (usize, Option) { - (0, Some(self.old_len - self.idx)) - } -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl Drop for DrainFilter<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ - fn drop(&mut self) { - struct BackshiftOnDrop<'a, 'b, T, F, A: Allocator> - where - F: FnMut(&mut T) -> bool, - { - drain: &'b mut DrainFilter<'a, T, F, A>, - } - - impl<'a, 'b, T, F, A: Allocator> Drop for BackshiftOnDrop<'a, 'b, T, F, A> - where - F: FnMut(&mut T) -> bool, - { - fn drop(&mut self) { - unsafe { - if self.drain.idx < self.drain.old_len && self.drain.del > 0 { - // This is a pretty messed up state, and there isn't really an - // obviously right thing to do. We don't want to keep trying - // to execute `pred`, so we just backshift all the unprocessed - // elements and tell the vec that they still exist. The backshift - // is required to prevent a double-drop of the last successfully - // drained item prior to a panic in the predicate. - let ptr = self.drain.vec.as_mut_ptr(); - let src = ptr.add(self.drain.idx); - let dst = src.sub(self.drain.del); - let tail_len = self.drain.old_len - self.drain.idx; - src.copy_to(dst, tail_len); - } - self.drain.vec.set_len(self.drain.old_len - self.drain.del); - } - } - } - - let backshift = BackshiftOnDrop { drain: self }; - - // Attempt to consume any remaining elements if the filter predicate - // has not yet panicked. We'll backshift any remaining elements - // whether we've already panicked or if the consumption here panics. - if !backshift.drain.panic_flag { - backshift.drain.for_each(drop); - } - } -} diff --git a/library/alloc/src/vec/partial_eq.rs b/library/alloc/src/vec/partial_eq.rs new file mode 100644 index 0000000000..ff90b6caf4 --- /dev/null +++ b/library/alloc/src/vec/partial_eq.rs @@ -0,0 +1,43 @@ +use crate::alloc::Allocator; +use crate::borrow::Cow; + +use super::Vec; + +macro_rules! __impl_slice_eq1 { + ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?, #[$stability:meta]) => { + #[$stability] + impl PartialEq<$rhs> for $lhs + where + T: PartialEq, + $($ty: $bound)? + { + #[inline] + fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } + #[inline] + fn ne(&self, other: &$rhs) -> bool { self[..] != other[..] } + } + } +} + +__impl_slice_eq1! { [A: Allocator] Vec, Vec, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [A: Allocator] Vec, &[U], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [A: Allocator] Vec, &mut [U], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [A: Allocator] &[T], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } +__impl_slice_eq1! { [A: Allocator] &mut [T], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } +__impl_slice_eq1! { [A: Allocator] Vec, [U], #[stable(feature = "partialeq_vec_for_slice", since = "1.48.0")] } +__impl_slice_eq1! { [A: Allocator] [T], Vec, #[stable(feature = "partialeq_vec_for_slice", since = "1.48.0")] } +__impl_slice_eq1! { [A: Allocator] Cow<'_, [T]>, Vec where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Cow<'_, [T]>, &[U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Cow<'_, [T]>, &mut [U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [A: Allocator, const N: usize] Vec, [U; N], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [A: Allocator, const N: usize] Vec, &[U; N], #[stable(feature = "rust1", since = "1.0.0")] } + +// NOTE: some less important impls are omitted to reduce code bloat +// FIXME(Centril): Reconsider this? +//__impl_slice_eq1! { [const N: usize] Vec, &mut [B; N], } +//__impl_slice_eq1! { [const N: usize] [A; N], Vec, } +//__impl_slice_eq1! { [const N: usize] &[A; N], Vec, } +//__impl_slice_eq1! { [const N: usize] &mut [A; N], Vec, } +//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, [B; N], } +//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &[B; N], } +//__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &mut [B; N], } diff --git a/library/alloc/src/vec/set_len_on_drop.rs b/library/alloc/src/vec/set_len_on_drop.rs new file mode 100644 index 0000000000..8b66bc8121 --- /dev/null +++ b/library/alloc/src/vec/set_len_on_drop.rs @@ -0,0 +1,28 @@ +// Set the length of the vec when the `SetLenOnDrop` value goes out of scope. +// +// The idea is: The length field in SetLenOnDrop is a local variable +// that the optimizer will see does not alias with any stores through the Vec's data +// pointer. This is a workaround for alias analysis issue #32155 +pub(super) struct SetLenOnDrop<'a> { + len: &'a mut usize, + local_len: usize, +} + +impl<'a> SetLenOnDrop<'a> { + #[inline] + pub(super) fn new(len: &'a mut usize) -> Self { + SetLenOnDrop { local_len: *len, len } + } + + #[inline] + pub(super) fn increment_len(&mut self, increment: usize) { + self.local_len += increment; + } +} + +impl Drop for SetLenOnDrop<'_> { + #[inline] + fn drop(&mut self) { + *self.len = self.local_len; + } +} diff --git a/library/alloc/src/vec/source_iter_marker.rs b/library/alloc/src/vec/source_iter_marker.rs new file mode 100644 index 0000000000..8c0e95559f --- /dev/null +++ b/library/alloc/src/vec/source_iter_marker.rs @@ -0,0 +1,108 @@ +use core::iter::{InPlaceIterable, SourceIter}; +use core::mem::{self, ManuallyDrop}; +use core::ptr::{self}; + +use super::{AsIntoIter, InPlaceDrop, SpecFromIter, SpecFromIterNested, Vec}; + +/// Specialization marker for collecting an iterator pipeline into a Vec while reusing the +/// source allocation, i.e. executing the pipeline in place. +/// +/// The SourceIter parent trait is necessary for the specializing function to access the allocation +/// which is to be reused. But it is not sufficient for the specialization to be valid. See +/// additional bounds on the impl. +#[rustc_unsafe_specialization_marker] +pub(super) trait SourceIterMarker: SourceIter {} + +// The std-internal SourceIter/InPlaceIterable traits are only implemented by chains of +// Adapter>> (all owned by core/std). Additional bounds +// on the adapter implementations (beyond `impl Trait for Adapter`) only depend on other +// traits already marked as specialization traits (Copy, TrustedRandomAccess, FusedIterator). +// I.e. the marker does not depend on lifetimes of user-supplied types. Modulo the Copy hole, which +// several other specializations already depend on. +impl SourceIterMarker for T where T: SourceIter + InPlaceIterable {} + +impl SpecFromIter for Vec +where + I: Iterator + SourceIterMarker, +{ + default fn from_iter(mut iterator: I) -> Self { + // Additional requirements which cannot expressed via trait bounds. We rely on const eval + // instead: + // a) no ZSTs as there would be no allocation to reuse and pointer arithmetic would panic + // b) size match as required by Alloc contract + // c) alignments match as required by Alloc contract + if mem::size_of::() == 0 + || mem::size_of::() + != mem::size_of::<<::Source as AsIntoIter>::Item>() + || mem::align_of::() + != mem::align_of::<<::Source as AsIntoIter>::Item>() + { + // fallback to more generic implementations + return SpecFromIterNested::from_iter(iterator); + } + + let (src_buf, src_ptr, dst_buf, dst_end, cap) = unsafe { + let inner = iterator.as_inner().as_into_iter(); + ( + inner.buf.as_ptr(), + inner.ptr, + inner.buf.as_ptr() as *mut T, + inner.end as *const T, + inner.cap, + ) + }; + + // use try-fold since + // - it vectorizes better for some iterator adapters + // - unlike most internal iteration methods, it only takes a &mut self + // - it lets us thread the write pointer through its innards and get it back in the end + let sink = InPlaceDrop { inner: dst_buf, dst: dst_buf }; + let sink = iterator + .try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(dst_end)) + .unwrap(); + // iteration succeeded, don't drop head + let dst = ManuallyDrop::new(sink).dst; + + let src = unsafe { iterator.as_inner().as_into_iter() }; + // check if SourceIter contract was upheld + // caveat: if they weren't we may not even make it to this point + debug_assert_eq!(src_buf, src.buf.as_ptr()); + // check InPlaceIterable contract. This is only possible if the iterator advanced the + // source pointer at all. If it uses unchecked access via TrustedRandomAccess + // then the source pointer will stay in its initial position and we can't use it as reference + if src.ptr != src_ptr { + debug_assert!( + dst as *const _ <= src.ptr, + "InPlaceIterable contract violation, write pointer advanced beyond read pointer" + ); + } + + // drop any remaining values at the tail of the source + src.drop_remaining(); + // but prevent drop of the allocation itself once IntoIter goes out of scope + src.forget_allocation(); + + let vec = unsafe { + let len = dst.offset_from(dst_buf) as usize; + Vec::from_raw_parts(dst_buf, len, cap) + }; + + vec + } +} + +fn write_in_place_with_drop( + src_end: *const T, +) -> impl FnMut(InPlaceDrop, T) -> Result, !> { + move |mut sink, item| { + unsafe { + // the InPlaceIterable contract cannot be verified precisely here since + // try_fold has an exclusive reference to the source pointer + // all we can do is check if it's still in range + debug_assert!(sink.dst as *const _ <= src_end, "InPlaceIterable contract violation"); + ptr::write(sink.dst, item); + sink.dst = sink.dst.add(1); + } + Ok(sink) + } +} diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs new file mode 100644 index 0000000000..b6186a7eba --- /dev/null +++ b/library/alloc/src/vec/spec_extend.rs @@ -0,0 +1,82 @@ +use crate::alloc::Allocator; +use core::iter::TrustedLen; +use core::ptr::{self}; +use core::slice::{self}; + +use super::{IntoIter, SetLenOnDrop, Vec}; + +// Specialization trait used for Vec::extend +pub(super) trait SpecExtend { + fn spec_extend(&mut self, iter: I); +} + +impl SpecExtend for Vec +where + I: Iterator, +{ + default fn spec_extend(&mut self, iter: I) { + self.extend_desugared(iter) + } +} + +impl SpecExtend for Vec +where + I: TrustedLen, +{ + default fn spec_extend(&mut self, iterator: I) { + // This is the case for a TrustedLen iterator. + let (low, high) = iterator.size_hint(); + if let Some(high_value) = high { + debug_assert_eq!( + low, + high_value, + "TrustedLen iterator's size hint is not exact: {:?}", + (low, high) + ); + } + if let Some(additional) = high { + self.reserve(additional); + unsafe { + let mut ptr = self.as_mut_ptr().add(self.len()); + let mut local_len = SetLenOnDrop::new(&mut self.len); + iterator.for_each(move |element| { + ptr::write(ptr, element); + ptr = ptr.offset(1); + // NB can't overflow since we would have had to alloc the address space + local_len.increment_len(1); + }); + } + } else { + self.extend_desugared(iterator) + } + } +} + +impl SpecExtend> for Vec { + fn spec_extend(&mut self, mut iterator: IntoIter) { + unsafe { + self.append_elements(iterator.as_slice() as _); + } + iterator.ptr = iterator.end; + } +} + +impl<'a, T: 'a, I, A: Allocator + 'a> SpecExtend<&'a T, I> for Vec +where + I: Iterator, + T: Clone, +{ + default fn spec_extend(&mut self, iterator: I) { + self.spec_extend(iterator.cloned()) + } +} + +impl<'a, T: 'a, A: Allocator + 'a> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec +where + T: Copy, +{ + fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { + let slice = iterator.as_slice(); + unsafe { self.append_elements(slice) }; + } +} diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs new file mode 100644 index 0000000000..de61017478 --- /dev/null +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -0,0 +1,60 @@ +use crate::alloc::Allocator; +use crate::raw_vec::RawVec; +use core::ptr::{self}; + +use super::{ExtendElement, IsZero, Vec}; + +// Specialization trait used for Vec::from_elem +pub(super) trait SpecFromElem: Sized { + fn from_elem(elem: Self, n: usize, alloc: A) -> Vec; +} + +impl SpecFromElem for T { + default fn from_elem(elem: Self, n: usize, alloc: A) -> Vec { + let mut v = Vec::with_capacity_in(n, alloc); + v.extend_with(n, ExtendElement(elem)); + v + } +} + +impl SpecFromElem for i8 { + #[inline] + fn from_elem(elem: i8, n: usize, alloc: A) -> Vec { + if elem == 0 { + return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; + } + unsafe { + let mut v = Vec::with_capacity_in(n, alloc); + ptr::write_bytes(v.as_mut_ptr(), elem as u8, n); + v.set_len(n); + v + } + } +} + +impl SpecFromElem for u8 { + #[inline] + fn from_elem(elem: u8, n: usize, alloc: A) -> Vec { + if elem == 0 { + return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; + } + unsafe { + let mut v = Vec::with_capacity_in(n, alloc); + ptr::write_bytes(v.as_mut_ptr(), elem, n); + v.set_len(n); + v + } + } +} + +impl SpecFromElem for T { + #[inline] + fn from_elem(elem: T, n: usize, alloc: A) -> Vec { + if elem.is_zero() { + return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; + } + let mut v = Vec::with_capacity_in(n, alloc); + v.extend_with(n, ExtendElement(elem)); + v + } +} diff --git a/library/alloc/src/vec/spec_from_iter.rs b/library/alloc/src/vec/spec_from_iter.rs new file mode 100644 index 0000000000..bbfcc68dae --- /dev/null +++ b/library/alloc/src/vec/spec_from_iter.rs @@ -0,0 +1,97 @@ +use core::mem::ManuallyDrop; +use core::ptr::{self}; +use core::slice::{self}; + +use super::{IntoIter, SpecExtend, SpecFromIterNested, Vec}; + +/// Specialization trait used for Vec::from_iter +/// +/// ## The delegation graph: +/// +/// ```text +/// +-------------+ +/// |FromIterator | +/// +-+-----------+ +/// | +/// v +/// +-+-------------------------------+ +---------------------+ +/// |SpecFromIter +---->+SpecFromIterNested | +/// |where I: | | |where I: | +/// | Iterator (default)----------+ | | Iterator (default) | +/// | vec::IntoIter | | | TrustedLen | +/// | SourceIterMarker---fallback-+ | | | +/// | slice::Iter | | | +/// | Iterator | +---------------------+ +/// +---------------------------------+ +/// ``` +pub(super) trait SpecFromIter { + fn from_iter(iter: I) -> Self; +} + +impl SpecFromIter for Vec +where + I: Iterator, +{ + default fn from_iter(iterator: I) -> Self { + SpecFromIterNested::from_iter(iterator) + } +} + +impl SpecFromIter> for Vec { + fn from_iter(iterator: IntoIter) -> Self { + // A common case is passing a vector into a function which immediately + // re-collects into a vector. We can short circuit this if the IntoIter + // has not been advanced at all. + // When it has been advanced We can also reuse the memory and move the data to the front. + // But we only do so when the resulting Vec wouldn't have more unused capacity + // than creating it through the generic FromIterator implementation would. That limitation + // is not strictly necessary as Vec's allocation behavior is intentionally unspecified. + // But it is a conservative choice. + let has_advanced = iterator.buf.as_ptr() as *const _ != iterator.ptr; + if !has_advanced || iterator.len() >= iterator.cap / 2 { + unsafe { + let it = ManuallyDrop::new(iterator); + if has_advanced { + ptr::copy(it.ptr, it.buf.as_ptr(), it.len()); + } + return Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap); + } + } + + let mut vec = Vec::new(); + // must delegate to spec_extend() since extend() itself delegates + // to spec_from for empty Vecs + vec.spec_extend(iterator); + vec + } +} + +impl<'a, T: 'a, I> SpecFromIter<&'a T, I> for Vec +where + I: Iterator, + T: Clone, +{ + default fn from_iter(iterator: I) -> Self { + SpecFromIter::from_iter(iterator.cloned()) + } +} + +// This utilizes `iterator.as_slice().to_vec()` since spec_extend +// must take more steps to reason about the final capacity + length +// and thus do more work. `to_vec()` directly allocates the correct amount +// and fills it exactly. +impl<'a, T: 'a + Clone> SpecFromIter<&'a T, slice::Iter<'a, T>> for Vec { + #[cfg(not(test))] + fn from_iter(iterator: slice::Iter<'a, T>) -> Self { + iterator.as_slice().to_vec() + } + + // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is + // required for this method definition, is not available. Instead use the + // `slice::to_vec` function which is only available with cfg(test) + // NB see the slice::hack module in slice.rs for more information + #[cfg(test)] + fn from_iter(iterator: slice::Iter<'a, T>) -> Self { + crate::slice::to_vec(iterator.as_slice(), crate::alloc::Global) + } +} diff --git a/library/alloc/src/vec/spec_from_iter_nested.rs b/library/alloc/src/vec/spec_from_iter_nested.rs new file mode 100644 index 0000000000..ec390c6216 --- /dev/null +++ b/library/alloc/src/vec/spec_from_iter_nested.rs @@ -0,0 +1,56 @@ +use core::iter::TrustedLen; +use core::ptr::{self}; + +use super::{SpecExtend, Vec}; + +/// Another specialization trait for Vec::from_iter +/// necessary to manually prioritize overlapping specializations +/// see [`SpecFromIter`](super::SpecFromIter) for details. +pub(super) trait SpecFromIterNested { + fn from_iter(iter: I) -> Self; +} + +impl SpecFromIterNested for Vec +where + I: Iterator, +{ + default fn from_iter(mut iterator: I) -> Self { + // Unroll the first iteration, as the vector is going to be + // expanded on this iteration in every case when the iterable is not + // empty, but the loop in extend_desugared() is not going to see the + // vector being full in the few subsequent loop iterations. + // So we get better branch prediction. + let mut vector = match iterator.next() { + None => return Vec::new(), + Some(element) => { + let (lower, _) = iterator.size_hint(); + let mut vector = Vec::with_capacity(lower.saturating_add(1)); + unsafe { + ptr::write(vector.as_mut_ptr(), element); + vector.set_len(1); + } + vector + } + }; + // must delegate to spec_extend() since extend() itself delegates + // to spec_from for empty Vecs + as SpecExtend>::spec_extend(&mut vector, iterator); + vector + } +} + +impl SpecFromIterNested for Vec +where + I: TrustedLen, +{ + fn from_iter(iterator: I) -> Self { + let mut vector = match iterator.size_hint() { + (_, Some(upper)) => Vec::with_capacity(upper), + _ => Vec::new(), + }; + // must delegate to spec_extend() since extend() itself delegates + // to spec_from for empty Vecs + vector.spec_extend(iterator); + vector + } +} diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs new file mode 100644 index 0000000000..0a27b5b62e --- /dev/null +++ b/library/alloc/src/vec/splice.rs @@ -0,0 +1,133 @@ +use crate::alloc::{Allocator, Global}; +use core::ptr::{self}; +use core::slice::{self}; + +use super::{Drain, Vec}; + +/// A splicing iterator for `Vec`. +/// +/// This struct is created by [`Vec::splice()`]. +/// See its documentation for more. +/// +/// # Example +/// +/// ``` +/// let mut v = vec![0, 1, 2]; +/// let new = [7, 8]; +/// let iter: std::vec::Splice<_> = v.splice(1.., new.iter().cloned()); +/// ``` +#[derive(Debug)] +#[stable(feature = "vec_splice", since = "1.21.0")] +pub struct Splice< + 'a, + I: Iterator + 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, +> { + pub(super) drain: Drain<'a, I::Item, A>, + pub(super) replace_with: I, +} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl Iterator for Splice<'_, I, A> { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.drain.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.drain.size_hint() + } +} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl DoubleEndedIterator for Splice<'_, I, A> { + fn next_back(&mut self) -> Option { + self.drain.next_back() + } +} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl ExactSizeIterator for Splice<'_, I, A> {} + +#[stable(feature = "vec_splice", since = "1.21.0")] +impl Drop for Splice<'_, I, A> { + fn drop(&mut self) { + self.drain.by_ref().for_each(drop); + + unsafe { + if self.drain.tail_len == 0 { + self.drain.vec.as_mut().extend(self.replace_with.by_ref()); + return; + } + + // First fill the range left by drain(). + if !self.drain.fill(&mut self.replace_with) { + return; + } + + // There may be more elements. Use the lower bound as an estimate. + // FIXME: Is the upper bound a better guess? Or something else? + let (lower_bound, _upper_bound) = self.replace_with.size_hint(); + if lower_bound > 0 { + self.drain.move_tail(lower_bound); + if !self.drain.fill(&mut self.replace_with) { + return; + } + } + + // Collect any remaining elements. + // This is a zero-length vector which does not allocate if `lower_bound` was exact. + let mut collected = self.replace_with.by_ref().collect::>().into_iter(); + // Now we have an exact count. + if collected.len() > 0 { + self.drain.move_tail(collected.len()); + let filled = self.drain.fill(&mut collected); + debug_assert!(filled); + debug_assert_eq!(collected.len(), 0); + } + } + // Let `Drain::drop` move the tail back if necessary and restore `vec.len`. + } +} + +/// Private helper methods for `Splice::drop` +impl Drain<'_, T, A> { + /// The range from `self.vec.len` to `self.tail_start` contains elements + /// that have been moved out. + /// Fill that range as much as possible with new elements from the `replace_with` iterator. + /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) + unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { + let vec = unsafe { self.vec.as_mut() }; + let range_start = vec.len; + let range_end = self.tail_start; + let range_slice = unsafe { + slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start) + }; + + for place in range_slice { + if let Some(new_item) = replace_with.next() { + unsafe { ptr::write(place, new_item) }; + vec.len += 1; + } else { + return false; + } + } + true + } + + /// Makes room for inserting more elements before the tail. + unsafe fn move_tail(&mut self, additional: usize) { + let vec = unsafe { self.vec.as_mut() }; + let len = self.tail_start + self.tail_len; + vec.buf.reserve(len, additional); + + let new_tail_start = self.tail_start + additional; + unsafe { + let src = vec.as_ptr().add(self.tail_start); + let dst = vec.as_mut_ptr().add(new_tail_start); + ptr::copy(src, dst, self.tail_len); + } + self.tail_start = new_tail_start; + } +} diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index b7cc03f8eb..dd98f80645 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -14,13 +14,13 @@ #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_drain_sorted)] #![feature(slice_ptr_get)] -#![feature(split_inclusive)] #![feature(binary_heap_retain)] -#![feature(deque_range)] #![feature(inplace_iteration)] #![feature(iter_map_while)] #![feature(int_bits_const)] #![feature(vecdeque_binary_search)] +#![feature(slice_group_by)] +#![feature(vec_extend_from_within)] use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs index a4f0fb415f..777c10b1bf 100644 --- a/library/alloc/tests/slice.rs +++ b/library/alloc/tests/slice.rs @@ -1898,3 +1898,61 @@ fn subslice_patterns() { m!(&mut v, [..] => ()); m!(&mut v, [x, .., y] => c!((x, y), (&mut N, &mut N), (&mut N(0), &mut N(4)))); } + +#[test] +fn test_group_by() { + let slice = &[1, 1, 1, 3, 3, 2, 2, 2, 1, 0]; + + let mut iter = slice.group_by(|a, b| a == b); + assert_eq!(iter.next(), Some(&[1, 1, 1][..])); + assert_eq!(iter.next(), Some(&[3, 3][..])); + assert_eq!(iter.next(), Some(&[2, 2, 2][..])); + assert_eq!(iter.next(), Some(&[1][..])); + assert_eq!(iter.next(), Some(&[0][..])); + assert_eq!(iter.next(), None); + + let mut iter = slice.group_by(|a, b| a == b); + assert_eq!(iter.next_back(), Some(&[0][..])); + assert_eq!(iter.next_back(), Some(&[1][..])); + assert_eq!(iter.next_back(), Some(&[2, 2, 2][..])); + assert_eq!(iter.next_back(), Some(&[3, 3][..])); + assert_eq!(iter.next_back(), Some(&[1, 1, 1][..])); + assert_eq!(iter.next_back(), None); + + let mut iter = slice.group_by(|a, b| a == b); + assert_eq!(iter.next(), Some(&[1, 1, 1][..])); + assert_eq!(iter.next_back(), Some(&[0][..])); + assert_eq!(iter.next(), Some(&[3, 3][..])); + assert_eq!(iter.next_back(), Some(&[1][..])); + assert_eq!(iter.next(), Some(&[2, 2, 2][..])); + assert_eq!(iter.next_back(), None); +} + +#[test] +fn test_group_by_mut() { + let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2, 1, 0]; + + let mut iter = slice.group_by_mut(|a, b| a == b); + assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); + assert_eq!(iter.next(), Some(&mut [3, 3][..])); + assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); + assert_eq!(iter.next(), Some(&mut [1][..])); + assert_eq!(iter.next(), Some(&mut [0][..])); + assert_eq!(iter.next(), None); + + let mut iter = slice.group_by_mut(|a, b| a == b); + assert_eq!(iter.next_back(), Some(&mut [0][..])); + assert_eq!(iter.next_back(), Some(&mut [1][..])); + assert_eq!(iter.next_back(), Some(&mut [2, 2, 2][..])); + assert_eq!(iter.next_back(), Some(&mut [3, 3][..])); + assert_eq!(iter.next_back(), Some(&mut [1, 1, 1][..])); + assert_eq!(iter.next_back(), None); + + let mut iter = slice.group_by_mut(|a, b| a == b); + assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); + assert_eq!(iter.next_back(), Some(&mut [0][..])); + assert_eq!(iter.next(), Some(&mut [3, 3][..])); + assert_eq!(iter.next_back(), Some(&mut [1][..])); + assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); + assert_eq!(iter.next_back(), None); +} diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index b28694186b..f3d74e0514 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -1,7 +1,11 @@ use std::borrow::Cow; +use std::cell::Cell; use std::collections::TryReserveError::*; +use std::ops::Bound; use std::ops::Bound::*; +use std::ops::RangeBounds; use std::panic; +use std::str; pub trait IntoCow<'a, B: ?Sized> where @@ -561,6 +565,52 @@ fn test_replace_range_unbounded() { assert_eq!(s, ""); } +#[test] +fn test_replace_range_evil_start_bound() { + struct EvilRange(Cell); + + impl RangeBounds for EvilRange { + fn start_bound(&self) -> Bound<&usize> { + Bound::Included(if self.0.get() { + &1 + } else { + self.0.set(true); + &0 + }) + } + fn end_bound(&self) -> Bound<&usize> { + Bound::Unbounded + } + } + + let mut s = String::from("🦀"); + s.replace_range(EvilRange(Cell::new(false)), ""); + assert_eq!(Ok(""), str::from_utf8(s.as_bytes())); +} + +#[test] +fn test_replace_range_evil_end_bound() { + struct EvilRange(Cell); + + impl RangeBounds for EvilRange { + fn start_bound(&self) -> Bound<&usize> { + Bound::Included(&0) + } + fn end_bound(&self) -> Bound<&usize> { + Bound::Excluded(if self.0.get() { + &3 + } else { + self.0.set(true); + &4 + }) + } + } + + let mut s = String::from("🦀"); + s.replace_range(EvilRange(Cell::new(false)), ""); + assert_eq!(Ok(""), str::from_utf8(s.as_bytes())); +} + #[test] fn test_extend_ref() { let mut a = "foo".to_string(); diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index e19406d7a0..5c7ff67bc6 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1954,3 +1954,73 @@ fn test_vec_swap() { assert_eq!(a[0], 42); assert_eq!(n, 0); } + +#[test] +fn test_extend_from_within_spec() { + #[derive(Copy)] + struct CopyOnly; + + impl Clone for CopyOnly { + fn clone(&self) -> Self { + panic!("extend_from_within must use specialization on copy"); + } + } + + vec![CopyOnly, CopyOnly].extend_from_within(..); +} + +#[test] +fn test_extend_from_within_clone() { + let mut v = vec![String::from("sssss"), String::from("12334567890"), String::from("c")]; + v.extend_from_within(1..); + + assert_eq!(v, ["sssss", "12334567890", "c", "12334567890", "c"]); +} + +#[test] +fn test_extend_from_within_complete_rande() { + let mut v = vec![0, 1, 2, 3]; + v.extend_from_within(..); + + assert_eq!(v, [0, 1, 2, 3, 0, 1, 2, 3]); +} + +#[test] +fn test_extend_from_within_empty_rande() { + let mut v = vec![0, 1, 2, 3]; + v.extend_from_within(1..1); + + assert_eq!(v, [0, 1, 2, 3]); +} + +#[test] +#[should_panic] +fn test_extend_from_within_out_of_rande() { + let mut v = vec![0, 1]; + v.extend_from_within(..3); +} + +#[test] +fn test_extend_from_within_zst() { + let mut v = vec![(); 8]; + v.extend_from_within(3..7); + + assert_eq!(v, [(); 12]); +} + +#[test] +fn test_extend_from_within_empty_vec() { + let mut v = Vec::::new(); + v.extend_from_within(..); + + assert_eq!(v, []); +} + +#[test] +fn test_extend_from_within() { + let mut v = vec![String::from("a"), String::from("b"), String::from("c")]; + v.extend_from_within(1..=2); + v.extend_from_within(..=1); + + assert_eq!(v, ["a", "b", "c", "b", "c", "a", "b"]); +} diff --git a/library/core/benches/iter.rs b/library/core/benches/iter.rs index fb6b4b7837..f2169914ac 100644 --- a/library/core/benches/iter.rs +++ b/library/core/benches/iter.rs @@ -276,7 +276,29 @@ bench_sums! { bench_sums! { bench_cycle_take_sum, bench_cycle_take_ref_sum, - (0i64..10000).cycle().take(1000000) + (0..10000).cycle().take(1000000) +} + +bench_sums! { + bench_cycle_skip_take_sum, + bench_cycle_skip_take_ref_sum, + (0..100000).cycle().skip(1000000).take(1000000) +} + +bench_sums! { + bench_cycle_take_skip_sum, + bench_cycle_take_skip_ref_sum, + (0..100000).cycle().take(1000000).skip(100000) +} + +bench_sums! { + bench_skip_cycle_skip_zip_add_sum, + bench_skip_cycle_skip_zip_add_ref_sum, + (0..100000).skip(100).cycle().skip(100) + .zip((0..100000).cycle().skip(10)) + .map(|(a,b)| a+b) + .skip(100000) + .take(1000000) } // Checks whether Skip> is as fast as Zip, Skip>, from diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index c198797e65..6ec0f0b5ff 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -53,6 +53,27 @@ use crate::ptr; /// * `Layout` queries and calculations in general must be correct. Callers of /// this trait are allowed to rely on the contracts defined on each method, /// and implementors must ensure such contracts remain true. +/// +/// * You may not rely on allocations actually happening, even if there are explicit +/// heap allocations in the source. The optimizer may detect unused allocations that it can either +/// eliminate entirely or move to the stack and thus never invoke the allocator. The +/// optimizer may further assume that allocation is infallible, so code that used to fail due +/// to allocator failures may now suddenly work because the optimizer worked around the +/// need for an allocation. More concretely, the following code example is unsound, irrespective +/// of whether your custom allocator allows counting how many allocations have happened. +/// +/// ```rust,ignore (unsound and has placeholders) +/// drop(Box::new(42)); +/// let number_of_heap_allocs = /* call private allocator API */; +/// unsafe { std::intrinsics::assume(number_of_heap_allocs > 0); } +/// ``` +/// +/// Note that the optimizations mentioned above are not the only +/// optimization that can be applied. You may generally not rely on heap allocations +/// happening if they can be removed without changing program behavior. +/// Whether allocations happen or not is not part of the program behavior, even if it +/// could be detected via an allocator that tracks allocations by printing or otherwise +/// having side effects. #[stable(feature = "global_alloc", since = "1.28.0")] pub unsafe trait GlobalAlloc { /// Allocate memory as described by the given `layout`. diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 997c1a16cc..9dc3f05dae 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -4,6 +4,12 @@ use crate::mem; use crate::num::NonZeroUsize; use crate::ptr::NonNull; +// While this function is used in one place and its implementation +// could be inlined, the previous attempts to do so made rustc +// slower: +// +// * https://github.com/rust-lang/rust/pull/72189 +// * https://github.com/rust-lang/rust/pull/79827 const fn size_align() -> (usize, usize) { (mem::size_of::(), mem::align_of::()) } diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index c1c993bbc5..06a761531b 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -342,7 +342,10 @@ pub unsafe trait Allocator { /// /// The returned adaptor also implements `Allocator` and will simply borrow this. #[inline(always)] - fn by_ref(&self) -> &Self { + fn by_ref(&self) -> &Self + where + Self: Sized, + { self } } diff --git a/library/core/src/any.rs b/library/core/src/any.rs index d1951fbbf1..eef8f2046d 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -14,6 +14,29 @@ //! //! [`Box`]: ../../std/boxed/struct.Box.html //! +//! # Smart pointers and `dyn Any` +//! +//! One piece of behavior to keep in mind when using `Any` as a trait object, +//! especially with types like `Box` or `Arc`, is that simply +//! calling `.type_id()` on the value will produce the `TypeId` of the +//! *container*, not the underlying trait object. This can be avoided by +//! converting the smart pointer into a `&dyn Any` instead, which will return +//! the object's `TypeId`. For example: +//! +//! ``` +//! use std::any::{Any, TypeId}; +//! +//! let boxed: Box = Box::new(3_i32); +//! +//! // You're more likely to want this: +//! let actual_id = (&*boxed).type_id(); +//! // ... than this: +//! let boxed_id = boxed.type_id(); +//! +//! assert_eq!(actual_id, TypeId::of::()); +//! assert_eq!(boxed_id, TypeId::of::>()); +//! ``` +//! //! # Examples //! //! Consider a situation where we want to log out a value passed to a function. diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 706f865b4d..535291471b 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -11,7 +11,7 @@ use crate::{ /// A by-value [array] iterator. /// /// [array]: ../../std/primitive.array.html -#[unstable(feature = "array_value_iter", issue = "65798")] +#[stable(feature = "array_value_iter", since = "1.51.0")] pub struct IntoIter { /// This is the array we are iterating over. /// @@ -38,10 +38,21 @@ pub struct IntoIter { impl IntoIter { /// Creates a new iterator over the given `array`. /// - /// *Note*: this method might never get stabilized and/or removed in the - /// future as there will likely be another, preferred way of obtaining this - /// iterator (either via `IntoIterator` for arrays or via another way). - #[unstable(feature = "array_value_iter", issue = "65798")] + /// *Note*: this method might be deprecated in the future, + /// after [`IntoIterator` is implemented for arrays][array-into-iter]. + /// + /// # Examples + /// + /// ``` + /// use std::array; + /// + /// for value in array::IntoIter::new([1, 2, 3, 4, 5]) { + /// // The type of `value` is a `i32` here, instead of `&i32` + /// let _: i32 = value; + /// } + /// ``` + /// [array-into-iter]: https://github.com/rust-lang/rust/pull/65819 + #[stable(feature = "array_value_iter", since = "1.51.0")] pub fn new(array: [T; N]) -> Self { // SAFETY: The transmute here is actually safe. The docs of `MaybeUninit` // promise: @@ -69,7 +80,7 @@ impl IntoIter { /// Returns an immutable slice of all elements that have not been yielded /// yet. - #[unstable(feature = "array_value_iter_slice", issue = "65798")] + #[stable(feature = "array_value_iter", since = "1.51.0")] pub fn as_slice(&self) -> &[T] { // SAFETY: We know that all elements within `alive` are properly initialized. unsafe { @@ -79,7 +90,7 @@ impl IntoIter { } /// Returns a mutable slice of all elements that have not been yielded yet. - #[unstable(feature = "array_value_iter_slice", issue = "65798")] + #[stable(feature = "array_value_iter", since = "1.51.0")] pub fn as_mut_slice(&mut self) -> &mut [T] { // SAFETY: We know that all elements within `alive` are properly initialized. unsafe { diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 71548bec7a..d13061d220 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -12,12 +12,13 @@ use crate::convert::{Infallible, TryFrom}; use crate::fmt; use crate::hash::{self, Hash}; use crate::marker::Unsize; +use crate::mem::MaybeUninit; use crate::ops::{Index, IndexMut}; use crate::slice::{Iter, IterMut}; mod iter; -#[unstable(feature = "array_value_iter", issue = "65798")] +#[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; /// Converts a reference to `T` into a reference to an array of length 1 (without copying). @@ -429,7 +430,6 @@ impl [T; N] { where F: FnMut(T) -> U, { - use crate::mem::MaybeUninit; struct Guard { dst: *mut T, initialized: usize, @@ -481,8 +481,6 @@ impl [T; N] { /// ``` #[unstable(feature = "array_zip", issue = "80094")] pub fn zip(self, rhs: [U; N]) -> [(T, U); N] { - use crate::mem::MaybeUninit; - let mut dst = MaybeUninit::uninit_array::(); for (i, (lhs, rhs)) in IntoIter::new(self).zip(IntoIter::new(rhs)).enumerate() { dst[i].write((lhs, rhs)); @@ -506,4 +504,75 @@ impl [T; N] { pub fn as_mut_slice(&mut self) -> &mut [T] { self } + + /// Borrows each element and returns an array of references with the same + /// size as `self`. + /// + /// + /// # Example + /// + /// ``` + /// #![feature(array_methods)] + /// + /// let floats = [3.1, 2.7, -1.0]; + /// let float_refs: [&f64; 3] = floats.each_ref(); + /// assert_eq!(float_refs, [&3.1, &2.7, &-1.0]); + /// ``` + /// + /// This method is particularly useful if combined with other methods, like + /// [`map`](#method.map). This way, you can can avoid moving the original + /// array if its elements are not `Copy`. + /// + /// ``` + /// #![feature(array_methods, array_map)] + /// + /// let strings = ["Ferris".to_string(), "♥".to_string(), "Rust".to_string()]; + /// let is_ascii = strings.each_ref().map(|s| s.is_ascii()); + /// assert_eq!(is_ascii, [true, false, true]); + /// + /// // We can still access the original array: it has not been moved. + /// assert_eq!(strings.len(), 3); + /// ``` + #[unstable(feature = "array_methods", issue = "76118")] + pub fn each_ref(&self) -> [&T; N] { + // Unlike in `map`, we don't need a guard here, as dropping a reference + // is a noop. + let mut out = MaybeUninit::uninit_array::(); + for (src, dst) in self.iter().zip(&mut out) { + dst.write(src); + } + + // SAFETY: All elements of `dst` are properly initialized and + // `MaybeUninit` has the same layout as `T`, so this cast is valid. + unsafe { (&mut out as *mut _ as *mut [&T; N]).read() } + } + + /// Borrows each element mutably and returns an array of mutable references + /// with the same size as `self`. + /// + /// + /// # Example + /// + /// ``` + /// #![feature(array_methods)] + /// + /// let mut floats = [3.1, 2.7, -1.0]; + /// let float_refs: [&mut f64; 3] = floats.each_mut(); + /// *float_refs[0] = 0.0; + /// assert_eq!(float_refs, [&mut 0.0, &mut 2.7, &mut -1.0]); + /// assert_eq!(floats, [0.0, 2.7, -1.0]); + /// ``` + #[unstable(feature = "array_methods", issue = "76118")] + pub fn each_mut(&mut self) -> [&mut T; N] { + // Unlike in `map`, we don't need a guard here, as dropping a reference + // is a noop. + let mut out = MaybeUninit::uninit_array::(); + for (src, dst) in self.iter_mut().zip(&mut out) { + dst.write(src); + } + + // SAFETY: All elements of `dst` are properly initialized and + // `MaybeUninit` has the same layout as `T`, so this cast is valid. + unsafe { (&mut out as *mut _ as *mut [&mut T; N]).read() } + } } diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index 6f5a6aa7c7..c9040cd0a1 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -40,7 +40,6 @@ /// provide a reference to related type `T`, it is often better to use /// [`AsRef`] as more types can safely implement it. /// -/// [`BorrowMut`]: BorrowMut /// [`Box`]: ../../std/boxed/struct.Box.html /// [`Mutex`]: ../../std/sync/struct.Mutex.html /// [`Rc`]: ../../std/rc/struct.Rc.html @@ -183,8 +182,6 @@ pub trait Borrow { /// As a companion to [`Borrow`] this trait allows a type to borrow as /// an underlying type by providing a mutable reference. See [`Borrow`] /// for more information on borrowing as another type. -/// -/// [`Borrow`]: Borrow #[stable(feature = "rust1", since = "1.0.0")] pub trait BorrowMut: Borrow { /// Mutably borrows from an owned value. diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c5ab7a39ff..885422732e 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -11,12 +11,10 @@ //! mutate it. //! //! Shareable mutable containers exist to permit mutability in a controlled manner, even in the -//! presence of aliasing. Both `Cell` and `RefCell` allow doing this in a single-threaded +//! presence of aliasing. Both [`Cell`] and [`RefCell`] allow doing this in a single-threaded //! way. However, neither `Cell` nor `RefCell` are thread safe (they do not implement -//! `Sync`). If you need to do aliasing and mutation between multiple threads it is possible to -//! use [`Mutex`](../../std/sync/struct.Mutex.html), -//! [`RwLock`](../../std/sync/struct.RwLock.html) or -//! [`atomic`](../../core/sync/atomic/index.html) types. +//! [`Sync`]). If you need to do aliasing and mutation between multiple threads it is possible to +//! use [`Mutex`], [`RwLock`] or [`atomic`] types. //! //! Values of the `Cell` and `RefCell` types may be mutated through shared references (i.e. //! the common `&T` type), whereas most Rust types can only be mutated through unique (`&mut T`) @@ -28,13 +26,14 @@ //! one must use the `RefCell` type, acquiring a write lock before mutating. `Cell` provides //! methods to retrieve and change the current interior value: //! -//! - For types that implement `Copy`, the `get` method retrieves the current interior value. -//! - For types that implement `Default`, the `take` method replaces the current interior value -//! with `Default::default()` and returns the replaced value. -//! - For all types, the `replace` method replaces the current interior value and returns the -//! replaced value and the `into_inner` method consumes the `Cell` and returns the interior -//! value. Additionally, the `set` method replaces the interior value, dropping the replaced -//! value. +//! - For types that implement [`Copy`], the [`get`](Cell::get) method retrieves the current +//! interior value. +//! - For types that implement [`Default`], the [`take`](Cell::take) method replaces the current +//! interior value with [`Default::default()`] and returns the replaced value. +//! - For all types, the [`replace`](Cell::replace) method replaces the current interior value and +//! returns the replaced value and the [`into_inner`](Cell::into_inner) method consumes the +//! `Cell` and returns the interior value. Additionally, the [`set`](Cell::set) method +//! replaces the interior value, dropping the replaced value. //! //! `RefCell` uses Rust's lifetimes to implement 'dynamic borrowing', a process whereby one can //! claim temporary, exclusive, mutable access to the inner value. Borrows for `RefCell`s are @@ -54,12 +53,12 @@ //! //! * Introducing mutability 'inside' of something immutable //! * Implementation details of logically-immutable methods. -//! * Mutating implementations of `Clone`. +//! * Mutating implementations of [`Clone`]. //! //! ## Introducing mutability 'inside' of something immutable //! -//! Many shared smart pointer types, including `Rc` and `Arc`, provide containers that can be -//! cloned and shared between multiple parties. Because the contained values may be +//! Many shared smart pointer types, including [`Rc`] and [`Arc`], provide containers that can +//! be cloned and shared between multiple parties. Because the contained values may be //! multiply-aliased, they can only be borrowed with `&`, not `&mut`. Without cells it would be //! impossible to mutate data inside of these smart pointers at all. //! @@ -91,7 +90,7 @@ //! ``` //! //! Note that this example uses `Rc` and not `Arc`. `RefCell`s are for single-threaded -//! scenarios. Consider using `RwLock` or `Mutex` if you need shared mutability in a +//! scenarios. Consider using [`RwLock`] or [`Mutex`] if you need shared mutability in a //! multi-threaded situation. //! //! ## Implementation details of logically-immutable methods @@ -127,10 +126,10 @@ //! ## Mutating implementations of `Clone` //! //! This is simply a special - but common - case of the previous: hiding mutability for operations -//! that appear to be immutable. The `clone` method is expected to not change the source value, and -//! is declared to take `&self`, not `&mut self`. Therefore, any mutation that happens in the -//! `clone` method must use cell types. For example, `Rc` maintains its reference counts within a -//! `Cell`. +//! that appear to be immutable. The [`clone`](Clone::clone) method is expected to not change the +//! source value, and is declared to take `&self`, not `&mut self`. Therefore, any mutation that +//! happens in the `clone` method must use cell types. For example, [`Rc`] maintains its +//! reference counts within a `Cell`. //! //! ``` //! use std::cell::Cell; @@ -185,6 +184,11 @@ //! } //! ``` //! +//! [`Arc`]: ../../std/sync/struct.Arc.html +//! [`Rc`]: ../../std/rc/struct.Rc.html +//! [`RwLock`]: ../../std/sync/struct.RwLock.html +//! [`Mutex`]: ../../std/sync/struct.Mutex.html +//! [`atomic`]: ../../core/sync/atomic/index.html #![stable(feature = "rust1", since = "1.0.0")] @@ -1261,6 +1265,40 @@ impl<'b, T: ?Sized> Ref<'b, T> { Ref { value: f(orig.value), borrow: orig.borrow } } + /// Makes a new `Ref` for an optional component of the borrowed data. The + /// original guard is returned as an `Err(..)` if the closure returns + /// `None`. + /// + /// The `RefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `Ref::filter_map(...)`. A method would interfere with methods of the same + /// name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_filter_map)] + /// + /// use std::cell::{RefCell, Ref}; + /// + /// let c = RefCell::new(vec![1, 2, 3]); + /// let b1: Ref> = c.borrow(); + /// let b2: Result, _> = Ref::filter_map(b1, |v| v.get(1)); + /// assert_eq!(*b2.unwrap(), 2); + /// ``` + #[unstable(feature = "cell_filter_map", reason = "recently added", issue = "81061")] + #[inline] + pub fn filter_map(orig: Ref<'b, T>, f: F) -> Result, Self> + where + F: FnOnce(&T) -> Option<&U>, + { + match f(orig.value) { + Some(value) => Ok(Ref { value, borrow: orig.borrow }), + None => Err(orig), + } + } + /// Splits a `Ref` into multiple `Ref`s for different components of the /// borrowed data. /// @@ -1372,6 +1410,58 @@ impl<'b, T: ?Sized> RefMut<'b, T> { RefMut { value: f(value), borrow } } + /// Makes a new `RefMut` for an optional component of the borrowed data. The + /// original guard is returned as an `Err(..)` if the closure returns + /// `None`. + /// + /// The `RefCell` is already mutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `RefMut::filter_map(...)`. A method would interfere with methods of the + /// same name on the contents of a `RefCell` used through `Deref`. + /// + /// # Examples + /// + /// ``` + /// #![feature(cell_filter_map)] + /// + /// use std::cell::{RefCell, RefMut}; + /// + /// let c = RefCell::new(vec![1, 2, 3]); + /// + /// { + /// let b1: RefMut> = c.borrow_mut(); + /// let mut b2: Result, _> = RefMut::filter_map(b1, |v| v.get_mut(1)); + /// + /// if let Ok(mut b2) = b2 { + /// *b2 += 2; + /// } + /// } + /// + /// assert_eq!(*c.borrow(), vec![1, 4, 3]); + /// ``` + #[unstable(feature = "cell_filter_map", reason = "recently added", issue = "81061")] + #[inline] + pub fn filter_map(orig: RefMut<'b, T>, f: F) -> Result, Self> + where + F: FnOnce(&mut T) -> Option<&mut U>, + { + // FIXME(nll-rfc#40): fix borrow-check + let RefMut { value, borrow } = orig; + let value = value as *mut T; + // SAFETY: function holds onto an exclusive reference for the duration + // of its call through `orig`, and the pointer is only de-referenced + // inside of the function call never allowing the exclusive reference to + // escape. + match f(unsafe { &mut *value }) { + Some(value) => Ok(RefMut { value, borrow }), + None => { + // SAFETY: same as above. + Err(RefMut { value: unsafe { &mut *value }, borrow }) + } + } + } + /// Splits a `RefMut` into multiple `RefMut`s for different components of the /// borrowed data. /// diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 394db5b591..7a0ec32cc6 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -47,6 +47,7 @@ use super::MAX; /// /// assert_eq!(None, c); /// ``` +#[doc(alias = "chr")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn from_u32(i: u32) -> Option { @@ -112,6 +113,48 @@ impl From for u32 { } } +#[stable(feature = "more_char_conversions", since = "1.51.0")] +impl From for u64 { + /// Converts a [`char`] into a [`u64`]. + /// + /// # Examples + /// + /// ``` + /// use std::mem; + /// + /// let c = '👤'; + /// let u = u64::from(c); + /// assert!(8 == mem::size_of_val(&u)) + /// ``` + #[inline] + fn from(c: char) -> Self { + // The char is casted to the value of the code point, then zero-extended to 64 bit. + // See [https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics] + c as u64 + } +} + +#[stable(feature = "more_char_conversions", since = "1.51.0")] +impl From for u128 { + /// Converts a [`char`] into a [`u128`]. + /// + /// # Examples + /// + /// ``` + /// use std::mem; + /// + /// let c = '⚙'; + /// let u = u128::from(c); + /// assert!(16 == mem::size_of_val(&u)) + /// ``` + #[inline] + fn from(c: char) -> Self { + // The char is casted to the value of the code point, then zero-extended to 128 bit. + // See [https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics] + c as u128 + } +} + /// Maps a byte in 0x00..=0xFF to a `char` whose code point has the same value, in U+0000..=U+00FF. /// /// Unicode is designed such that this effectively decodes bytes diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 0c459a820c..4a15b185a8 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -29,16 +29,20 @@ use self::Ordering::*; /// /// This trait allows for partial equality, for types that do not have a full /// equivalence relation. For example, in floating point numbers `NaN != NaN`, -/// so floating point types implement `PartialEq` but not [`Eq`]. +/// so floating point types implement `PartialEq` but not [`trait@Eq`]. /// -/// Formally, the equality must be (for all `a`, `b` and `c`): +/// Formally, the equality must be (for all `a`, `b`, `c` of type `A`, `B`, +/// `C`): /// -/// - symmetric: `a == b` implies `b == a`; and -/// - transitive: `a == b` and `b == c` implies `a == c`. +/// - **Symmetric**: if `A: PartialEq` and `B: PartialEq`, then **`a == b` +/// implies `b == a`**; and /// -/// Note that these requirements mean that the trait itself must be implemented -/// symmetrically and transitively: if `T: PartialEq` and `U: PartialEq` -/// then `U: PartialEq` and `T: PartialEq`. +/// - **Transitive**: if `A: PartialEq` and `B: PartialEq` and `A: +/// PartialEq`, then **`a == b` and `b == c` implies `a == c`**. +/// +/// Note that the `B: PartialEq` (symmetric) and `A: PartialEq` +/// (transitive) impls are not forced to exist, but these requirements apply +/// whenever they do exist. /// /// ## Derivable /// @@ -541,7 +545,7 @@ impl Ordering { /// assert_eq!(result, Ordering::Equal); /// /// let x: (i64, i64, i64) = (1, 2, 7); - /// let y: (i64, i64, i64) = (1, 5, 3); + /// let y: (i64, i64, i64) = (1, 5, 3); /// let result = x.0.cmp(&y.0).then_with(|| x.1.cmp(&y.1)).then_with(|| x.2.cmp(&y.2)); /// /// assert_eq!(result, Ordering::Less); diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 3f7110b34c..e1b19e4b73 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -135,8 +135,6 @@ pub const fn identity(x: T) -> T { /// Since both [`String`] and [`&str`] implement `AsRef` we can accept both as input argument. /// /// [`&str`]: primitive@str -/// [`Option`]: Option -/// [`Result`]: Result /// [`Borrow`]: crate::borrow::Borrow /// [`Eq`]: crate::cmp::Eq /// [`Ord`]: crate::cmp::Ord @@ -169,9 +167,6 @@ pub trait AsRef { /// **Note: This trait must not fail**. If the conversion can fail, use a /// dedicated method which returns an [`Option`] or a [`Result`]. /// -/// [`Option`]: Option -/// [`Result`]: Result -/// /// # Generic Implementations /// /// - `AsMut` auto-dereferences if the inner type is a mutable reference @@ -270,10 +265,9 @@ pub trait AsMut { /// is_hello(s); /// ``` /// -/// [`Option`]: Option -/// [`Result`]: Result /// [`String`]: ../../std/string/struct.String.html /// [`Vec`]: ../../std/vec/struct.Vec.html +#[rustc_diagnostic_item = "into_trait"] #[stable(feature = "rust1", since = "1.0.0")] pub trait Into: Sized { /// Performs the conversion. @@ -359,8 +353,6 @@ pub trait Into: Sized { /// } /// ``` /// -/// [`Option`]: Option -/// [`Result`]: Result /// [`String`]: ../../std/string/struct.String.html /// [`from`]: From::from /// [book]: ../../book/ch09-00-error-handling.html @@ -391,6 +383,7 @@ pub trait From: Sized { /// /// This suffers the same restrictions and reasoning as implementing /// [`Into`], see there for details. +#[rustc_diagnostic_item = "try_into_trait"] #[stable(feature = "try_from", since = "1.34.0")] pub trait TryInto: Sized { /// The type returned in the event of a conversion error. @@ -469,9 +462,9 @@ pub trait TryInto: Sized { /// assert!(try_successful_smaller_number.is_ok()); /// ``` /// -/// [`i32::MAX`]: crate::i32::MAX /// [`try_from`]: TryFrom::try_from /// [`!`]: ../../std/primitive.never.html +#[rustc_diagnostic_item = "try_from_trait"] #[stable(feature = "try_from", since = "1.34.0")] pub trait TryFrom: Sized { /// The type returned in the event of a conversion error. @@ -623,6 +616,14 @@ impl AsRef for str { } } +#[stable(feature = "as_mut_str_for_str", since = "1.51.0")] +impl AsMut for str { + #[inline] + fn as_mut(&mut self) -> &mut str { + self + } +} + //////////////////////////////////////////////////////////////////////////////// // THE NO-ERROR ERROR TYPE //////////////////////////////////////////////////////////////////////////////// diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 9a8d65cd4e..28ec327945 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -173,9 +173,11 @@ macro_rules! default_impl { impl Default for $t { #[inline] #[doc = $doc] - fn default() -> $t { $v } + fn default() -> $t { + $v + } } - } + }; } default_impl! { (), (), "Returns the default value of `()`" } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 0c65c1c9eb..73cf5d138b 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -456,7 +456,9 @@ impl Display for Arguments<'_> { /// /// When used with the alternate format specifier `#?`, the output is pretty-printed. /// -/// For more information on formatters, see [the module-level documentation][self]. +/// For more information on formatters, see [the module-level documentation][module]. +/// +/// [module]: ../../std/fmt/index.html /// /// This trait can be used with `#[derive]` if all fields implement `Debug`. When /// `derive`d for structs, it will use the name of the `struct`, then `{`, then a @@ -602,7 +604,9 @@ pub use macros::Debug; /// `Display` is similar to [`Debug`], but `Display` is for user-facing /// output, and so cannot be derived. /// -/// For more information on formatters, see [the module-level documentation][self]. +/// For more information on formatters, see [the module-level documentation][module]. +/// +/// [module]: ../../std/fmt/index.html /// /// # Examples /// @@ -674,7 +678,9 @@ pub trait Display { /// /// The alternate flag, `#`, adds a `0o` in front of the output. /// -/// For more information on formatters, see [the module-level documentation][self]. +/// For more information on formatters, see [the module-level documentation][module]. +/// +/// [module]: ../../std/fmt/index.html /// /// # Examples /// @@ -726,7 +732,9 @@ pub trait Octal { /// /// The alternate flag, `#`, adds a `0b` in front of the output. /// -/// For more information on formatters, see [the module-level documentation][self]. +/// For more information on formatters, see [the module-level documentation][module]. +/// +/// [module]: ../../std/fmt/index.html /// /// # Examples /// @@ -782,7 +790,9 @@ pub trait Binary { /// /// The alternate flag, `#`, adds a `0x` in front of the output. /// -/// For more information on formatters, see [the module-level documentation][self]. +/// For more information on formatters, see [the module-level documentation][module]. +/// +/// [module]: ../../std/fmt/index.html /// /// # Examples /// @@ -835,7 +845,9 @@ pub trait LowerHex { /// /// The alternate flag, `#`, adds a `0x` in front of the output. /// -/// For more information on formatters, see [the module-level documentation][self]. +/// For more information on formatters, see [the module-level documentation][module]. +/// +/// [module]: ../../std/fmt/index.html /// /// # Examples /// @@ -883,7 +895,9 @@ pub trait UpperHex { /// The `Pointer` trait should format its output as a memory location. This is commonly presented /// as hexadecimal. /// -/// For more information on formatters, see [the module-level documentation][self]. +/// For more information on formatters, see [the module-level documentation][module]. +/// +/// [module]: ../../std/fmt/index.html /// /// # Examples /// @@ -932,7 +946,9 @@ pub trait Pointer { /// /// The `LowerExp` trait should format its output in scientific notation with a lower-case `e`. /// -/// For more information on formatters, see [the module-level documentation][self]. +/// For more information on formatters, see [the module-level documentation][module]. +/// +/// [module]: ../../std/fmt/index.html /// /// # Examples /// @@ -981,7 +997,9 @@ pub trait LowerExp { /// /// The `UpperExp` trait should format its output in scientific notation with an upper-case `E`. /// -/// For more information on formatters, see [the module-level documentation][self]. +/// For more information on formatters, see [the module-level documentation][module]. +/// +/// [module]: ../../std/fmt/index.html /// /// # Examples /// @@ -1555,7 +1573,7 @@ impl<'a> Formatter<'a> { /// } /// } /// - /// // We set alignment to the left with ">". + /// // We set alignment to the right with ">". /// assert_eq!(&format!("{:G>3}", Foo), "GGG"); /// assert_eq!(&format!("{:t>6}", Foo), "tttttt"); /// ``` diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 7a98210995..cdd731fdd4 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -643,25 +643,42 @@ fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::R } /// Partition of `n` into n > 1e19 and rem <= 1e19 +/// +/// Integer division algorithm is based on the following paper: +/// +/// T. Granlund and P. Montgomery, “Division by Invariant Integers Using Multiplication” +/// in Proc. of the SIGPLAN94 Conference on Programming Language Design and +/// Implementation, 1994, pp. 61–72 +/// fn udiv_1e19(n: u128) -> (u128, u64) { const DIV: u64 = 1e19 as u64; - let high = (n >> 64) as u64; - if high == 0 { - let low = n as u64; - return ((low / DIV) as u128, low % DIV); - } - let sr = 65 - high.leading_zeros(); - let mut q = n << (128 - sr); - let mut r = n >> sr; - let mut carry = 0; - - for _ in 0..sr { - r = (r << 1) | (q >> 127); - q = (q << 1) | carry as u128; - - let s = (DIV as u128).wrapping_sub(r).wrapping_sub(1) as i128 >> 127; - carry = (s & 1) as u64; - r -= (DIV as u128) & s as u128; - } - ((q << 1) | carry as u128, r as u64) + const FACTOR: u128 = 156927543384667019095894735580191660403; + + let quot = if n < 1 << 83 { + ((n >> 19) as u64 / (DIV >> 19)) as u128 + } else { + u128_mulhi(n, FACTOR) >> 62 + }; + + let rem = (n - quot * DIV as u128) as u64; + (quot, rem) +} + +/// Multiply unsigned 128 bit integers, return upper 128 bits of the result +#[inline] +fn u128_mulhi(x: u128, y: u128) -> u128 { + let x_lo = x as u64; + let x_hi = (x >> 64) as u64; + let y_lo = y as u64; + let y_hi = (y >> 64) as u64; + + // handle possibility of overflow + let carry = (x_lo as u128 * y_lo as u128) >> 64; + let m = x_lo as u128 * y_hi as u128 + carry; + let high1 = m >> 64; + + let m_lo = m as u64; + let high2 = (x_hi as u128 * y_lo as u128 + m_lo as u128) >> 64; + + x_hi as u128 * y_hi as u128 + high1 + high2 } diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 979a5f8cf5..313729581a 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -91,7 +91,7 @@ pub const unsafe fn unreachable_unchecked() -> ! { /// }; /// /// // Back on our current thread, we wait for the value to be set -/// while live.load(Ordering::Acquire) { +/// while !live.load(Ordering::Acquire) { /// // The spin loop is a hint to the CPU that we're waiting, but probably /// // not for very long /// hint::spin_loop(); diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 5292182269..7c1a9b82f9 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -712,8 +712,8 @@ extern "rust-intrinsic" { /// [`std::process::abort`](../../std/process/fn.abort.html). pub fn abort() -> !; - /// Tells LLVM that this point in the code is not reachable, enabling - /// further optimizations. + /// Informs the optimizer that this point in the code is not reachable, + /// enabling further optimizations. /// /// N.B., this is very different from the `unreachable!()` macro: Unlike the /// macro, which panics when it is executed, it is *undefined behavior* to @@ -768,13 +768,6 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] pub fn size_of() -> usize; - /// Moves a value to an uninitialized memory location. - /// - /// Drop glue is not run on the destination. - /// - /// The stabilized version of this intrinsic is [`core::ptr::write`](crate::ptr::write). - pub fn move_val_init(dst: *mut T, src: T); - /// The minimum alignment of a type. /// /// The stabilized version of this intrinsic is [`core::mem::align_of`](crate::mem::align_of). @@ -1133,7 +1126,7 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. pub fn volatile_copy_nonoverlapping_memory(dst: *mut T, src: *const T, count: usize); /// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with - /// a size of `count` * `size_of::()` and an alignment of + /// a size of `count * size_of::()` and an alignment of /// `min_align_of::()` /// /// The volatile parameter is set to `true`, so it will not be optimized out @@ -1142,7 +1135,7 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. pub fn volatile_copy_memory(dst: *mut T, src: *const T, count: usize); /// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a - /// size of `count` * `size_of::()` and an alignment of + /// size of `count * size_of::()` and an alignment of /// `min_align_of::()`. /// /// The volatile parameter is set to `true`, so it will not be optimized out @@ -1588,7 +1581,7 @@ extern "rust-intrinsic" { pub fn exact_div(x: T, y: T) -> T; /// Performs an unchecked division, resulting in undefined behavior - /// where y = 0 or x = `T::MIN` and y = -1 + /// where `y == 0` or `x == T::MIN && y == -1` /// /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_div` method. For example, @@ -1596,7 +1589,7 @@ extern "rust-intrinsic" { #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] pub fn unchecked_div(x: T, y: T) -> T; /// Returns the remainder of an unchecked division, resulting in - /// undefined behavior where y = 0 or x = `T::MIN` and y = -1 + /// undefined behavior when `y == 0` or `x == T::MIN && y == -1` /// /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_rem` method. For example, @@ -1605,7 +1598,7 @@ extern "rust-intrinsic" { pub fn unchecked_rem(x: T, y: T) -> T; /// Performs an unchecked left shift, resulting in undefined behavior when - /// y < 0 or y >= N, where N is the width of T in bits. + /// `y < 0` or `y >= N`, where N is the width of T in bits. /// /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shl` method. For example, @@ -1613,7 +1606,7 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] pub fn unchecked_shl(x: T, y: T) -> T; /// Performs an unchecked right shift, resulting in undefined behavior when - /// y < 0 or y >= N, where N is the width of T in bits. + /// `y < 0` or `y >= N`, where N is the width of T in bits. /// /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shr` method. For example, @@ -1680,14 +1673,14 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] pub fn wrapping_mul(a: T, b: T) -> T; - /// Computes `a + b`, while saturating at numeric bounds. + /// Computes `a + b`, saturating at numeric bounds. /// /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_add` method. For example, /// [`u32::saturating_add`] #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] pub fn saturating_add(a: T, b: T) -> T; - /// Computes `a - b`, while saturating at numeric bounds. + /// Computes `a - b`, saturating at numeric bounds. /// /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_sub` method. For example, @@ -1696,14 +1689,14 @@ extern "rust-intrinsic" { pub fn saturating_sub(a: T, b: T) -> T; /// Returns the value of the discriminant for the variant in 'v', - /// cast to a `u64`; if `T` has no discriminant, returns 0. + /// cast to a `u64`; if `T` has no discriminant, returns `0`. /// /// The stabilized version of this intrinsic is [`core::mem::discriminant`](crate::mem::discriminant). #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] pub fn discriminant_value(v: &T) -> ::Discriminant; /// Returns the number of variants of the type `T` cast to a `usize`; - /// if `T` has no variants, returns 0. Uninhabited variants will be counted. + /// if `T` has no variants, returns `0`. Uninhabited variants will be counted. /// /// The to-be-stabilized version of this intrinsic is [`mem::variant_count`]. #[rustc_const_unstable(feature = "variant_count", issue = "73662")] @@ -1736,7 +1729,6 @@ extern "rust-intrinsic" { /// Allocate at compile time. Should not be called at runtime. #[rustc_const_unstable(feature = "const_heap", issue = "79597")] - #[cfg(not(bootstrap))] pub fn const_allocate(size: usize, align: usize) -> *mut u8; } @@ -1846,20 +1838,23 @@ pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) - /// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append #[doc(alias = "memcpy")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] #[inline] -pub unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { +pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); } - if cfg!(debug_assertions) + // FIXME: Perform these checks only at run time + /*if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst) && is_nonoverlapping(src, dst, count)) { // Not panicking to keep codegen impact smaller. abort(); - } + }*/ // SAFETY: the safety contract for `copy_nonoverlapping` must be // upheld by the caller. @@ -1928,16 +1923,19 @@ pub unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { /// ``` #[doc(alias = "memmove")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] #[inline] -pub unsafe fn copy(src: *const T, dst: *mut T, count: usize) { +pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { + #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] fn copy(src: *const T, dst: *mut T, count: usize); } - if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) { + // FIXME: Perform these checks only at run time + /*if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) { // Not panicking to keep codegen impact smaller. abort(); - } + }*/ // SAFETY: the safety contract for `copy` must be upheld by the caller. unsafe { copy(src, dst, count) } diff --git a/library/core/src/iter/adapters/chain.rs b/library/core/src/iter/adapters/chain.rs index 9753e1b43b..ce5e9936bb 100644 --- a/library/core/src/iter/adapters/chain.rs +++ b/library/core/src/iter/adapters/chain.rs @@ -1,5 +1,5 @@ use crate::iter::{DoubleEndedIterator, FusedIterator, Iterator, TrustedLen}; -use crate::{ops::Try, usize}; +use crate::ops::Try; /// An iterator that links two iterators together, in a chain. /// diff --git a/library/core/src/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs index 29e191db0f..081f282edc 100644 --- a/library/core/src/iter/adapters/flatten.rs +++ b/library/core/src/iter/adapters/flatten.rs @@ -265,7 +265,13 @@ where } } match self.iter.next() { - None => return self.backiter.as_mut()?.next(), + None => match self.backiter.as_mut()?.next() { + None => { + self.backiter = None; + return None; + } + elt @ Some(_) => return elt, + }, Some(inner) => self.frontiter = Some(inner.into_iter()), } } @@ -353,7 +359,13 @@ where } } match self.iter.next_back() { - None => return self.frontiter.as_mut()?.next_back(), + None => match self.frontiter.as_mut()?.next_back() { + None => { + self.frontiter = None; + return None; + } + elt @ Some(_) => return elt, + }, next => self.backiter = next.map(IntoIterator::into_iter), } } diff --git a/library/core/src/iter/adapters/fuse.rs b/library/core/src/iter/adapters/fuse.rs index ae07406531..7a852c2cb9 100644 --- a/library/core/src/iter/adapters/fuse.rs +++ b/library/core/src/iter/adapters/fuse.rs @@ -1,6 +1,8 @@ use crate::intrinsics; use crate::iter::adapters::{zip::try_get_unchecked, InPlaceIterable, SourceIter}; -use crate::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedRandomAccess}; +use crate::iter::{ + DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedLen, TrustedRandomAccess, +}; use crate::ops::Try; /// An iterator that yields `None` forever after the underlying iterator @@ -182,8 +184,19 @@ where } } +#[unstable(feature = "trusted_len", issue = "37572")] +// SAFETY: `TrustedLen` requires that an accurate length is reported via `size_hint()`. As `Fuse` +// is just forwarding this to the wrapped iterator `I` this property is preserved and it is safe to +// implement `TrustedLen` here. +unsafe impl TrustedLen for Fuse where I: TrustedLen {} + #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] +// SAFETY: `TrustedRandomAccess` requires that `size_hint()` must be exact and cheap to call, and +// `Iterator::__iterator_get_unchecked()` must be implemented accordingly. +// +// This is safe to implement as `Fuse` is just forwarding these to the wrapped iterator `I`, which +// preserves these properties. unsafe impl TrustedRandomAccess for Fuse where I: TrustedRandomAccess, diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs new file mode 100644 index 0000000000..d8bbd424cf --- /dev/null +++ b/library/core/src/iter/adapters/intersperse.rs @@ -0,0 +1,187 @@ +use super::Peekable; + +/// An iterator adapter that places a separator between all elements. +/// +/// This `struct` is created by [`Iterator::intersperse`]. See its documentation +/// for more information. +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[derive(Debug, Clone)] +pub struct Intersperse +where + I::Item: Clone, +{ + separator: I::Item, + iter: Peekable, + needs_sep: bool, +} + +impl Intersperse +where + I::Item: Clone, +{ + pub(in crate::iter) fn new(iter: I, separator: I::Item) -> Self { + Self { iter: iter.peekable(), separator, needs_sep: false } + } +} + +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +impl Iterator for Intersperse +where + I: Iterator, + I::Item: Clone, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + if self.needs_sep && self.iter.peek().is_some() { + self.needs_sep = false; + Some(self.separator.clone()) + } else { + self.needs_sep = true; + self.iter.next() + } + } + + fn fold(self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + let separator = self.separator; + intersperse_fold(self.iter, init, f, move || separator.clone(), self.needs_sep) + } + + fn size_hint(&self) -> (usize, Option) { + intersperse_size_hint(&self.iter, self.needs_sep) + } +} + +/// An iterator adapter that places a separator between all elements. +/// +/// This `struct` is created by [`Iterator::intersperse_with`]. See its +/// documentation for more information. +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +pub struct IntersperseWith +where + I: Iterator, +{ + separator: G, + iter: Peekable, + needs_sep: bool, +} + +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +impl crate::fmt::Debug for IntersperseWith +where + I: Iterator + crate::fmt::Debug, + I::Item: crate::fmt::Debug, + G: crate::fmt::Debug, +{ + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + f.debug_struct("IntersperseWith") + .field("separator", &self.separator) + .field("iter", &self.iter) + .field("needs_sep", &self.needs_sep) + .finish() + } +} + +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +impl crate::clone::Clone for IntersperseWith +where + I: Iterator + crate::clone::Clone, + I::Item: crate::clone::Clone, + G: Clone, +{ + fn clone(&self) -> Self { + IntersperseWith { + separator: self.separator.clone(), + iter: self.iter.clone(), + needs_sep: self.needs_sep.clone(), + } + } +} + +impl IntersperseWith +where + I: Iterator, + G: FnMut() -> I::Item, +{ + pub(in crate::iter) fn new(iter: I, separator: G) -> Self { + Self { iter: iter.peekable(), separator, needs_sep: false } + } +} + +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +impl Iterator for IntersperseWith +where + I: Iterator, + G: FnMut() -> I::Item, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + if self.needs_sep && self.iter.peek().is_some() { + self.needs_sep = false; + Some((self.separator)()) + } else { + self.needs_sep = true; + self.iter.next() + } + } + + fn fold(self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + intersperse_fold(self.iter, init, f, self.separator, self.needs_sep) + } + + fn size_hint(&self) -> (usize, Option) { + intersperse_size_hint(&self.iter, self.needs_sep) + } +} + +fn intersperse_size_hint(iter: &I, needs_sep: bool) -> (usize, Option) +where + I: Iterator, +{ + let (lo, hi) = iter.size_hint(); + let next_is_elem = !needs_sep; + ( + lo.saturating_sub(next_is_elem as usize).saturating_add(lo), + hi.and_then(|hi| hi.saturating_sub(next_is_elem as usize).checked_add(hi)), + ) +} + +fn intersperse_fold( + mut iter: I, + init: B, + mut f: F, + mut separator: G, + needs_sep: bool, +) -> B +where + I: Iterator, + F: FnMut(B, I::Item) -> B, + G: FnMut() -> I::Item, +{ + let mut accum = init; + + if !needs_sep { + if let Some(x) = iter.next() { + accum = f(accum, x); + } else { + return accum; + } + } + + iter.fold(accum, |mut accum, x| { + accum = f(accum, separator()); + accum = f(accum, x); + accum + }) +} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index b8d3430f91..41a7b13232 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -11,6 +11,7 @@ mod filter_map; mod flatten; mod fuse; mod inspect; +mod intersperse; mod map; mod map_while; mod peekable; @@ -41,6 +42,9 @@ pub use self::flatten::Flatten; #[stable(feature = "iter_copied", since = "1.36.0")] pub use self::copied::Copied; +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +pub use self::intersperse::{Intersperse, IntersperseWith}; + #[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] pub use self::map_while::MapWhile; diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index 2f8b9653c5..43301444e3 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -265,7 +265,6 @@ impl Peekable { /// # Examples /// Consume a number if it's equal to 0. /// ``` - /// #![feature(peekable_next_if)] /// let mut iter = (0..5).peekable(); /// // The first item of the iterator is 0; consume it. /// assert_eq!(iter.next_if(|&x| x == 0), Some(0)); @@ -277,14 +276,13 @@ impl Peekable { /// /// Consume any number less than 10. /// ``` - /// #![feature(peekable_next_if)] /// let mut iter = (1..20).peekable(); /// // Consume all numbers less than 10 /// while iter.next_if(|&x| x < 10).is_some() {} /// // The next value returned will be 10 /// assert_eq!(iter.next(), Some(10)); /// ``` - #[unstable(feature = "peekable_next_if", issue = "72480")] + #[stable(feature = "peekable_next_if", since = "1.51.0")] pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option { match self.next() { Some(matched) if func(&matched) => Some(matched), @@ -302,7 +300,6 @@ impl Peekable { /// # Example /// Consume a number if it's equal to 0. /// ``` - /// #![feature(peekable_next_if)] /// let mut iter = (0..5).peekable(); /// // The first item of the iterator is 0; consume it. /// assert_eq!(iter.next_if_eq(&0), Some(0)); @@ -311,7 +308,7 @@ impl Peekable { /// // `next_if_eq` saves the value of the next item if it was not equal to `expected`. /// assert_eq!(iter.next(), Some(1)); /// ``` - #[unstable(feature = "peekable_next_if", issue = "72480")] + #[stable(feature = "peekable_next_if", since = "1.51.0")] pub fn next_if_eq(&mut self, expected: &T) -> Option where T: ?Sized, diff --git a/library/core/src/iter/adapters/skip.rs b/library/core/src/iter/adapters/skip.rs index dd5325660c..e55c7a6bf5 100644 --- a/library/core/src/iter/adapters/skip.rs +++ b/library/core/src/iter/adapters/skip.rs @@ -1,3 +1,4 @@ +use crate::intrinsics::unlikely; use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable}; use crate::ops::{ControlFlow, Try}; @@ -31,13 +32,10 @@ where #[inline] fn next(&mut self) -> Option { - if self.n == 0 { - self.iter.next() - } else { - let old_n = self.n; - self.n = 0; - self.iter.nth(old_n) + if unlikely(self.n > 0) { + self.iter.nth(crate::mem::take(&mut self.n) - 1); } + self.iter.next() } #[inline] diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 8cd4c77523..98b8dca961 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -286,6 +286,7 @@ where #[inline] unsafe fn get_unchecked(&mut self, idx: usize) -> ::Item { + let idx = self.index + idx; // SAFETY: the caller must uphold the contract for // `Iterator::__iterator_get_unchecked`. unsafe { (self.a.__iterator_get_unchecked(idx), self.b.__iterator_get_unchecked(idx)) } @@ -397,7 +398,7 @@ impl ZipFmt Self { // In debug builds, trigger a panic on overflow. // This should optimize completely out in release builds. @@ -211,6 +212,7 @@ macro_rules! step_identical_methods { } #[inline] + #[allow(arithmetic_overflow)] fn backward(start: Self, n: usize) -> Self { // In debug builds, trigger a panic on overflow. // This should optimize completely out in release builds. diff --git a/library/core/src/iter/traits/exact_size.rs b/library/core/src/iter/traits/exact_size.rs index eadbdf45c7..996d62e2b4 100644 --- a/library/core/src/iter/traits/exact_size.rs +++ b/library/core/src/iter/traits/exact_size.rs @@ -91,6 +91,7 @@ pub trait ExactSizeIterator: Iterator { /// /// assert_eq!(5, five.len()); /// ``` + #[doc(alias = "length")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn len(&self) -> usize { diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 7ba16f8928..6b42d45620 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -8,7 +8,7 @@ use crate::ops::{Add, ControlFlow, Try}; use super::super::TrustedRandomAccess; use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; -use super::super::{FromIterator, Product, Sum, Zip}; +use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip}; use super::super::{ Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, }; @@ -225,8 +225,6 @@ pub trait Iterator { /// This function might panic if the iterator has more than [`usize::MAX`] /// elements. /// - /// [`usize::MAX`]: crate::usize::MAX - /// /// # Examples /// /// Basic usage: @@ -569,6 +567,106 @@ pub trait Iterator { Zip::new(self, other.into_iter()) } + /// Creates a new iterator which places a copy of `separator` between adjacent + /// items of the original iterator. + /// + /// In case `separator` does not implement [`Clone`] or needs to be + /// computed every time, use [`intersperse_with`]. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_intersperse)] + /// + /// let mut a = [0, 1, 2].iter().intersperse(&100); + /// assert_eq!(a.next(), Some(&0)); // The first element from `a`. + /// assert_eq!(a.next(), Some(&100)); // The separator. + /// assert_eq!(a.next(), Some(&1)); // The next element from `a`. + /// assert_eq!(a.next(), Some(&100)); // The separator. + /// assert_eq!(a.next(), Some(&2)); // The last element from `a`. + /// assert_eq!(a.next(), None); // The iterator is finished. + /// ``` + /// + /// `intersperse` can be very useful to join an iterator's items using a common element: + /// ``` + /// #![feature(iter_intersperse)] + /// + /// let hello = ["Hello", "World", "!"].iter().copied().intersperse(" ").collect::(); + /// assert_eq!(hello, "Hello World !"); + /// ``` + /// + /// [`Clone`]: crate::clone::Clone + /// [`intersperse_with`]: Iterator::intersperse_with + #[inline] + #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] + fn intersperse(self, separator: Self::Item) -> Intersperse + where + Self: Sized, + Self::Item: Clone, + { + Intersperse::new(self, separator) + } + + /// Creates a new iterator which places an item generated by `separator` + /// between adjacent items of the original iterator. + /// + /// The closure will be called exactly once each time an item is placed + /// between two adjacent items from the underlying iterator; specifically, + /// the closure is not called if the underlying iterator yields less than + /// two items and after the last item is yielded. + /// + /// If the iterator's item implements [`Clone`], it may be easier to use + /// [`intersperse`]. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_intersperse)] + /// + /// #[derive(PartialEq, Debug)] + /// struct NotClone(usize); + /// + /// let v = vec![NotClone(0), NotClone(1), NotClone(2)]; + /// let mut it = v.into_iter().intersperse_with(|| NotClone(99)); + /// + /// assert_eq!(it.next(), Some(NotClone(0))); // The first element from `v`. + /// assert_eq!(it.next(), Some(NotClone(99))); // The separator. + /// assert_eq!(it.next(), Some(NotClone(1))); // The next element from `v`. + /// assert_eq!(it.next(), Some(NotClone(99))); // The separator. + /// assert_eq!(it.next(), Some(NotClone(2))); // The last element from from `v`. + /// assert_eq!(it.next(), None); // The iterator is finished. + /// ``` + /// + /// `intersperse_with` can be used in situations where the separator needs + /// to be computed: + /// ``` + /// #![feature(iter_intersperse)] + /// + /// let src = ["Hello", "to", "all", "people", "!!"].iter().copied(); + /// + /// // The closure mutably borrows its context to generate an item. + /// let mut happy_emojis = [" ❤️ ", " 😀 "].iter().copied(); + /// let separator = || happy_emojis.next().unwrap_or(" 🦀 "); + /// + /// let result = src.intersperse_with(separator).collect::(); + /// assert_eq!(result, "Hello ❤️ to 😀 all 🦀 people 🦀 !!"); + /// ``` + /// [`Clone`]: crate::clone::Clone + /// [`intersperse`]: Iterator::intersperse + #[inline] + #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] + fn intersperse_with(self, separator: G) -> IntersperseWith + where + Self: Sized, + G: FnMut() -> Self::Item, + { + IntersperseWith::new(self, separator) + } + /// Takes a closure and creates an iterator which calls that closure on each /// element. /// @@ -783,8 +881,6 @@ pub trait Iterator { /// assert_eq!(iter.next(), Some(5)); /// assert_eq!(iter.next(), None); /// ``` - /// - /// [`Option`]: Option #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn filter_map(self, f: F) -> FilterMap @@ -818,7 +914,6 @@ pub trait Iterator { /// overflow a [`usize`]. /// /// [`usize`]: type@usize - /// [`usize::MAX`]: crate::usize::MAX /// [`zip`]: Iterator::zip /// /// # Examples @@ -1118,7 +1213,7 @@ pub trait Iterator { /// the iteration should stop, but wasn't placed back into the iterator. /// /// Note that unlike [`take_while`] this iterator is **not** fused. - /// It is also not specified what this iterator returns after the first` None` is returned. + /// It is also not specified what this iterator returns after the first [`None`] is returned. /// If you need fused iterator, use [`fuse`]. /// /// [`fuse`]: Iterator::fuse @@ -1933,7 +2028,8 @@ pub trait Iterator { self.try_fold((), call(f)) } - /// An iterator method that applies a function, producing a single, final value. + /// Folds every element into an accumulator by applying an operation, + /// returning the final result. /// /// `fold()` takes two arguments: an initial value, and a closure with two /// arguments: an 'accumulator', and an element. The closure returns the value that @@ -1954,6 +2050,9 @@ pub trait Iterator { /// may not terminate for infinite iterators, even on traits for which a /// result is determinable in finite time. /// + /// Note: [`reduce()`] can be used to use the first element as the initial + /// value, if the accumulator type and item type is the same. + /// /// # Note to Implementors /// /// Several of the other (forward) methods have default implementations in @@ -2009,6 +2108,8 @@ pub trait Iterator { /// // they're the same /// assert_eq!(result, result2); /// ``` + /// + /// [`reduce()`]: Iterator::reduce #[doc(alias = "reduce")] #[doc(alias = "inject")] #[inline] @@ -2025,10 +2126,15 @@ pub trait Iterator { accum } - /// The same as [`fold()`], but uses the first element in the - /// iterator as the initial value, folding every subsequent element into it. - /// If the iterator is empty, return [`None`]; otherwise, return the result - /// of the fold. + /// Reduces the elements to a single one, by repeatedly applying a reducing + /// operation. + /// + /// If the iterator is empty, returns [`None`]; otherwise, returns the + /// result of the reduction. + /// + /// For iterators with at least one element, this is the same as [`fold()`] + /// with the first element of the iterator as the initial value, folding + /// every subsequent element into it. /// /// [`fold()`]: Iterator::fold /// @@ -2037,13 +2143,11 @@ pub trait Iterator { /// Find the maximum value: /// /// ``` - /// #![feature(iterator_fold_self)] - /// /// fn find_max(iter: I) -> Option /// where I: Iterator, /// I::Item: Ord, /// { - /// iter.fold_first(|a, b| { + /// iter.reduce(|a, b| { /// if a >= b { a } else { b } /// }) /// } @@ -2054,8 +2158,8 @@ pub trait Iterator { /// assert_eq!(find_max(b.iter()), None); /// ``` #[inline] - #[unstable(feature = "iterator_fold_self", issue = "68125")] - fn fold_first(mut self, f: F) -> Option + #[stable(feature = "iterator_fold_self", since = "1.51.0")] + fn reduce(mut self, f: F) -> Option where Self: Sized, F: FnMut(Self::Item, Self::Item) -> Self::Item, @@ -2330,7 +2434,6 @@ pub trait Iterator { /// non-matching elements. /// /// [`Some(index)`]: Some - /// [`usize::MAX`]: crate::usize::MAX /// /// # Examples /// @@ -2553,7 +2656,7 @@ pub trait Iterator { move |x, y| cmp::max_by(x, y, &mut compare) } - self.fold_first(fold(compare)) + self.reduce(fold(compare)) } /// Returns the element that gives the minimum value from the @@ -2613,7 +2716,7 @@ pub trait Iterator { move |x, y| cmp::min_by(x, y, &mut compare) } - self.fold_first(fold(compare)) + self.reduce(fold(compare)) } /// Reverses an iterator's direction. diff --git a/library/core/src/iter/traits/marker.rs b/library/core/src/iter/traits/marker.rs index 0900676146..c4e21b8486 100644 --- a/library/core/src/iter/traits/marker.rs +++ b/library/core/src/iter/traits/marker.rs @@ -33,8 +33,6 @@ impl FusedIterator for &mut I {} /// /// This trait must only be implemented when the contract is upheld. Consumers /// of this trait must inspect [`Iterator::size_hint()`]’s upper bound. -/// -/// [`usize::MAX`]: crate::usize::MAX #[unstable(feature = "trusted_len", issue = "37572")] #[rustc_unsafe_specialization_marker] pub unsafe trait TrustedLen: Iterator {} @@ -45,12 +43,14 @@ unsafe impl TrustedLen for &mut I {} /// An iterator that when yielding an item will have taken at least one element /// from its underlying [`SourceIter`]. /// -/// Calling [`next()`] guarantees that at least one value of the iterator's underlying source -/// has been moved out and the result of the iterator chain could be inserted in its place, -/// assuming structural constraints of the source allow such an insertion. +/// Calling any method that advances the iterator, e.g. [`next()`] or [`try_fold()`], +/// guarantees that for each step at least one value of the iterator's underlying source +/// has been moved out and the result of the iterator chain could be inserted +/// in its place, assuming structural constraints of the source allow such an insertion. /// In other words this trait indicates that an iterator pipeline can be collected in place. /// /// [`SourceIter`]: crate::iter::SourceIter /// [`next()`]: Iterator::next +/// [`try_fold()`]: Iterator::try_fold #[unstable(issue = "none", feature = "inplace_iteration")] pub unsafe trait InPlaceIterable: Iterator {} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 5b19bf6b80..a69b840e4b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -62,17 +62,17 @@ #![warn(missing_docs)] #![warn(missing_debug_implementations)] #![allow(explicit_outlives_requirements)] -#![allow(incomplete_features)] #![feature(rustc_allow_const_fn_unstable)] #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] #![feature(asm)] #![feature(cfg_target_has_atomic)] -#![cfg_attr(not(bootstrap), feature(const_heap))] +#![feature(const_heap)] #![feature(const_alloc_layout)] #![feature(const_assert_type)] #![feature(const_discriminant)] #![feature(const_cell_into_inner)] +#![feature(const_intrinsic_copy)] #![feature(const_checked_int_methods)] #![feature(const_euclidean_int_methods)] #![feature(const_float_classify)] @@ -88,11 +88,11 @@ #![feature(const_impl_trait)] #![feature(const_fn_floating_point_arithmetic)] #![feature(const_fn_fn_ptr_basics)] -#![feature(const_generics)] #![feature(const_option)] #![feature(const_precise_live_drops)] #![feature(const_ptr_offset)] #![feature(const_ptr_offset_from)] +#![feature(const_ptr_read)] #![feature(const_raw_ptr_comparison)] #![feature(const_raw_ptr_deref)] #![feature(const_slice_from_raw_parts)] @@ -111,6 +111,7 @@ #![feature(doc_spotlight)] #![feature(duration_consts_2)] #![feature(duration_saturating_ops)] +#![feature(extended_key_value_attributes)] #![feature(extern_types)] #![feature(fundamental)] #![feature(intrinsics)] @@ -122,13 +123,13 @@ #![feature(nll)] #![feature(exhaustive_patterns)] #![feature(no_core)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![feature(or_patterns)] #![feature(prelude_import)] #![feature(repr_simd, platform_intrinsics)] #![feature(rustc_attrs)] #![feature(simd_ffi)] +#![cfg_attr(bootstrap, feature(min_const_generics))] #![feature(min_specialization)] #![feature(staged_api)] #![feature(std_internals)] @@ -252,6 +253,8 @@ pub mod panicking; pub mod pin; pub mod raw; pub mod result; +#[unstable(feature = "async_stream", issue = "79024")] +pub mod stream; pub mod sync; pub mod fmt; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 0699c9eab1..6a7e4b2ba2 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1,8 +1,9 @@ +#[cfg(bootstrap)] #[doc(include = "panic.md")] #[macro_export] -#[allow_internal_unstable(core_panic, const_caller_location)] +#[allow_internal_unstable(core_panic)] #[stable(feature = "core", since = "1.6.0")] -#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "core_panic_macro")] +#[rustc_diagnostic_item = "core_panic_macro"] macro_rules! panic { () => ( $crate::panic!("explicit panic") @@ -18,6 +19,21 @@ macro_rules! panic { ); } +#[cfg(not(bootstrap))] +#[doc(include = "panic.md")] +#[macro_export] +#[rustc_builtin_macro = "core_panic"] +#[allow_internal_unstable(edition_panic)] +#[stable(feature = "core", since = "1.6.0")] +#[rustc_diagnostic_item = "core_panic_macro"] +macro_rules! panic { + // Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021` + // depending on the edition of the caller. + ($($arg:tt)*) => { + /* compiler built-in */ + }; +} + /// Asserts that two expressions are equal to each other (using [`PartialEq`]). /// /// On panic, this macro will print the values of the expressions with their @@ -163,7 +179,7 @@ macro_rules! assert_ne { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "debug_assert_macro")] +#[rustc_diagnostic_item = "debug_assert_macro"] macro_rules! debug_assert { ($($arg:tt)*) => (if $crate::cfg!(debug_assertions) { $crate::assert!($($arg)*); }) } @@ -401,7 +417,7 @@ macro_rules! write { /// For more information, see [`write!`]. For information on the format string syntax, see /// [`std::fmt`]. /// -/// [`std::fmt`]: crate::fmt +/// [`std::fmt`]: ../std/fmt/index.html /// /// # Examples /// @@ -730,7 +746,7 @@ pub(crate) mod builtin { /// [`Display`]: crate::fmt::Display /// [`Debug`]: crate::fmt::Debug /// [`fmt::Arguments`]: crate::fmt::Arguments - /// [`std::fmt`]: crate::fmt + /// [`std::fmt`]: ../std/fmt/index.html /// [`format!`]: ../std/macro.format.html /// [`println!`]: ../std/macro.println.html /// @@ -1194,7 +1210,7 @@ pub(crate) mod builtin { /// be provided with or without arguments for formatting. See [`std::fmt`] /// for syntax for this form. /// - /// [`std::fmt`]: crate::fmt + /// [`std::fmt`]: ../std/fmt/index.html /// /// # Examples /// @@ -1217,8 +1233,8 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] #[macro_export] - #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "assert_macro")] - #[allow_internal_unstable(core_panic)] + #[rustc_diagnostic_item = "assert_macro"] + #[allow_internal_unstable(core_panic, edition_panic)] macro_rules! assert { ($cond:expr $(,)?) => {{ /* compiler built-in */ }}; ($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }}; diff --git a/library/core/src/macros/panic.md b/library/core/src/macros/panic.md index a02e74d5e5..6e502426df 100644 --- a/library/core/src/macros/panic.md +++ b/library/core/src/macros/panic.md @@ -10,22 +10,23 @@ tests. `panic!` is closely tied with the `unwrap` method of both `panic!` when they are set to [`None`] or [`Err`] variants. This macro is used to inject panic into a Rust thread, causing the thread to -panic entirely. Each thread's panic can be reaped as the [`Box`]`<`[`Any`]`>` type, -and the single-argument form of the `panic!` macro will be the value which -is transmitted. +panic entirely. This macro panics with a string and uses the [`format!`] syntax +for building the message. + +Each thread's panic can be reaped as the [`Box`]`<`[`Any`]`>` type, +which contains either a `&str` or `String` for regular `panic!()` invocations. +To panic with a value of another other type, [`panic_any`] can be used. [`Result`] enum is often a better solution for recovering from errors than using the `panic!` macro. This macro should be used to avoid proceeding using incorrect values, such as from external sources. Detailed information about error handling is found in the [book]. -The multi-argument form of this macro panics with a string and has the -[`format!`] syntax for building a string. - See also the macro [`compile_error!`], for raising errors during compilation. [ounwrap]: Option::unwrap [runwrap]: Result::unwrap +[`panic_any`]: ../std/panic/fn.panic_any.html [`Box`]: ../std/boxed/struct.Box.html [`Any`]: crate::any::Any [`format!`]: ../std/macro.format.html @@ -42,6 +43,6 @@ program with code `101`. # #![allow(unreachable_code)] panic!(); panic!("this is a terrible mistake!"); -panic!(4); // panic with the value of 4 to be collected elsewhere panic!("this is a {} {message}", "fancy", message = "message"); +std::panic::panic_any(4); // panic with the value of 4 to be collected elsewhere ``` diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 57e0bb1499..3760f5c479 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -172,11 +172,42 @@ use crate::ptr; /// /// ## Initializing a struct field-by-field /// -/// There is currently no supported way to create a raw pointer or reference -/// to a field of a struct inside `MaybeUninit`. That means it is not possible -/// to create a struct by calling `MaybeUninit::uninit::()` and then writing -/// to its fields. +/// You can use `MaybeUninit`, and the [`std::ptr::addr_of_mut`] macro, to initialize structs field by field: /// +/// ```rust +/// use std::mem::MaybeUninit; +/// use std::ptr::addr_of_mut; +/// +/// #[derive(Debug, PartialEq)] +/// pub struct Foo { +/// name: String, +/// list: Vec, +/// } +/// +/// let foo = { +/// let mut uninit: MaybeUninit = MaybeUninit::uninit(); +/// let ptr = uninit.as_mut_ptr(); +/// +/// // Initializing the `name` field +/// unsafe { addr_of_mut!((*ptr).name).write("Bob".to_string()); } +/// +/// // Initializing the `list` field +/// // If there is a panic here, then the `String` in the `name` field leaks. +/// unsafe { addr_of_mut!((*ptr).list).write(vec![0, 1, 2]); } +/// +/// // All the fields are initialized, so we call `assume_init` to get an initialized Foo. +/// unsafe { uninit.assume_init() } +/// }; +/// +/// assert_eq!( +/// foo, +/// Foo { +/// name: "Bob".to_string(), +/// list: vec![0, 1, 2] +/// } +/// ); +/// ``` +/// [`std::ptr::addr_of_mut`]: crate::ptr::addr_of_mut /// [ub]: ../../reference/behavior-considered-undefined.html /// /// # Layout @@ -575,8 +606,9 @@ impl MaybeUninit { /// // they both get dropped! /// ``` #[unstable(feature = "maybe_uninit_extra", issue = "63567")] + #[rustc_const_unstable(feature = "maybe_uninit_extra", issue = "63567")] #[inline(always)] - pub unsafe fn assume_init_read(&self) -> T { + pub const unsafe fn assume_init_read(&self) -> T { // SAFETY: the caller must guarantee that `self` is initialized. // Reading from `self.as_ptr()` is safe since `self` should be initialized. unsafe { @@ -803,6 +835,46 @@ impl MaybeUninit { } } + /// Extracts the values from an array of `MaybeUninit` containers. + /// + /// # Safety + /// + /// It is up to the caller to guarantee that all elements of the array are + /// in an initialized state. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_uninit_array)] + /// #![feature(maybe_uninit_array_assume_init)] + /// use std::mem::MaybeUninit; + /// + /// let mut array: [MaybeUninit; 3] = MaybeUninit::uninit_array(); + /// array[0] = MaybeUninit::new(0); + /// array[1] = MaybeUninit::new(1); + /// array[2] = MaybeUninit::new(2); + /// + /// // SAFETY: Now safe as we initialised all elements + /// let array = unsafe { + /// MaybeUninit::array_assume_init(array) + /// }; + /// + /// assert_eq!(array, [0, 1, 2]); + /// ``` + #[unstable(feature = "maybe_uninit_array_assume_init", issue = "80908")] + #[inline(always)] + pub unsafe fn array_assume_init(array: [Self; N]) -> [T; N] { + // SAFETY: + // * The caller guarantees that all elements of the array are initialized + // * `MaybeUninit` and T are guaranteed to have the same layout + // * MaybeUnint does not drop, so there are no double-frees + // And thus the conversion is safe + unsafe { + intrinsics::assert_inhabited::<[T; N]>(); + (&array as *const _ as *const [T; N]).read() + } + } + /// Assuming all the elements are initialized, get a slice to them. /// /// # Safety diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index e84014c68a..778e34e634 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -4,6 +4,7 @@ //! types, initializing and manipulating memory. #![stable(feature = "rust1", since = "1.0.0")] +#![cfg_attr(bootstrap, allow(unused_unsafe))] use crate::clone; use crate::cmp; @@ -151,9 +152,14 @@ pub const fn forget(t: T) { #[inline] #[unstable(feature = "forget_unsized", issue = "none")] pub fn forget_unsized(t: T) { + #[cfg(bootstrap)] // SAFETY: the forget intrinsic could be safe, but there's no point in making it safe since // we'll be implementing this function soon via `ManuallyDrop` - unsafe { intrinsics::forget(t) } + unsafe { + intrinsics::forget(t) + } + #[cfg(not(bootstrap))] + intrinsics::forget(t) } /// Returns the size of a type in bytes. @@ -328,7 +334,8 @@ pub const fn size_of() -> usize { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] pub const fn size_of_val(val: &T) -> usize { - intrinsics::size_of_val(val) + // SAFETY: `val` is a reference, so it's a valid raw pointer + unsafe { intrinsics::size_of_val(val) } } /// Returns the size of the pointed-to value in bytes. @@ -374,8 +381,10 @@ pub const fn size_of_val(val: &T) -> usize { /// ``` #[inline] #[unstable(feature = "layout_for_ptr", issue = "69835")] -pub unsafe fn size_of_val_raw(val: *const T) -> usize { - intrinsics::size_of_val(val) +#[rustc_const_unstable(feature = "const_size_of_val_raw", issue = "46571")] +pub const unsafe fn size_of_val_raw(val: *const T) -> usize { + // SAFETY: the caller must provide a valid raw pointer + unsafe { intrinsics::size_of_val(val) } } /// Returns the [ABI]-required minimum alignment of a type. @@ -419,7 +428,8 @@ pub fn min_align_of() -> usize { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_deprecated(reason = "use `align_of_val` instead", since = "1.2.0")] pub fn min_align_of_val(val: &T) -> usize { - intrinsics::min_align_of_val(val) + // SAFETY: val is a reference, so it's a valid raw pointer + unsafe { intrinsics::min_align_of_val(val) } } /// Returns the [ABI]-required minimum alignment of a type. @@ -463,7 +473,8 @@ pub const fn align_of() -> usize { #[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] #[allow(deprecated)] pub const fn align_of_val(val: &T) -> usize { - intrinsics::min_align_of_val(val) + // SAFETY: val is a reference, so it's a valid raw pointer + unsafe { intrinsics::min_align_of_val(val) } } /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. @@ -505,8 +516,10 @@ pub const fn align_of_val(val: &T) -> usize { /// ``` #[inline] #[unstable(feature = "layout_for_ptr", issue = "69835")] -pub unsafe fn align_of_val_raw(val: *const T) -> usize { - intrinsics::min_align_of_val(val) +#[rustc_const_unstable(feature = "const_align_of_val_raw", issue = "46571")] +pub const unsafe fn align_of_val_raw(val: *const T) -> usize { + // SAFETY: the caller must provide a valid raw pointer + unsafe { intrinsics::min_align_of_val(val) } } /// Returns `true` if dropping values of type `T` matters. @@ -643,7 +656,6 @@ pub unsafe fn zeroed() -> T { /// (Notice that the rules around uninitialized integers are not finalized yet, but /// until they are, it is advisable to avoid them.) /// -/// [`MaybeUninit`]: MaybeUninit /// [uninit]: MaybeUninit::uninit /// [assume_init]: MaybeUninit::assume_init /// [inv]: MaybeUninit#initialization-invariant @@ -877,6 +889,7 @@ pub fn replace(dest: &mut T, mut src: T) -> T { /// ``` /// /// [`RefCell`]: crate::cell::RefCell +#[doc(alias = "delete")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn drop(_x: T) {} diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index 039112e9f3..20ac165c6c 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -3,7 +3,7 @@ //! # Problem statement //! //! We are given a decimal string such as `12.34e56`. This string consists of integral (`12`), -//! fractional (`45`), and exponent (`56`) parts. All parts are optional and interpreted as zero +//! fractional (`34`), and exponent (`56`) parts. All parts are optional and interpreted as zero //! when missing. //! //! We seek the IEEE 754 floating point number that is closest to the exact value of the decimal @@ -332,7 +332,7 @@ fn bound_intermediate_digits(decimal: &Decimal<'_>, e: i64) -> u64 { // It tries to find a positive number k such that `f << k / 10^e` is an in-range // significand. This will result in about `2^53 * f * 10^e` < `10^17 * f * 10^e`. // One input that triggers this is 0.33...33 (375 x 3). - f_len + (e.abs() as u64) + 17 + f_len + e.unsigned_abs() + 17 } } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 4d876fd8c3..795f94ec03 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1,12 +1,13 @@ -//! This module provides constants which are specific to the implementation -//! of the `f32` floating point data type. +//! Constants specific to the `f32` single-precision floating point type. //! //! *[See also the `f32` primitive type](../../std/primitive.f32.html).* //! //! Mathematically significant numbers are provided in the `consts` sub-module. //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! For the constants defined directly in this module +//! (as distinct from those defined in the `consts` sub-module), +//! new code should instead use the associated constants +//! defined directly on the `f32` type. #![stable(feature = "rust1", since = "1.0.0")] @@ -23,12 +24,14 @@ use crate::num::FpCategory; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let r = std::f32::RADIX; /// /// // intended way /// let r = f32::RADIX; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "TBD", reason = "replaced by the `RADIX` associated constant on `f32`")] pub const RADIX: u32 = f32::RADIX; /// Number of significant digits in base 2. @@ -38,12 +41,17 @@ pub const RADIX: u32 = f32::RADIX; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let d = std::f32::MANTISSA_DIGITS; /// /// // intended way /// let d = f32::MANTISSA_DIGITS; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MANTISSA_DIGITS` associated constant on `f32`" +)] pub const MANTISSA_DIGITS: u32 = f32::MANTISSA_DIGITS; /// Approximate number of significant digits in base 10. @@ -53,12 +61,14 @@ pub const MANTISSA_DIGITS: u32 = f32::MANTISSA_DIGITS; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let d = std::f32::DIGITS; /// /// // intended way /// let d = f32::DIGITS; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "TBD", reason = "replaced by the `DIGITS` associated constant on `f32`")] pub const DIGITS: u32 = f32::DIGITS; /// [Machine epsilon] value for `f32`. @@ -72,12 +82,17 @@ pub const DIGITS: u32 = f32::DIGITS; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let e = std::f32::EPSILON; /// /// // intended way /// let e = f32::EPSILON; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `EPSILON` associated constant on `f32`" +)] pub const EPSILON: f32 = f32::EPSILON; /// Smallest finite `f32` value. @@ -87,12 +102,14 @@ pub const EPSILON: f32 = f32::EPSILON; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let min = std::f32::MIN; /// /// // intended way /// let min = f32::MIN; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "TBD", reason = "replaced by the `MIN` associated constant on `f32`")] pub const MIN: f32 = f32::MIN; /// Smallest positive normal `f32` value. @@ -102,12 +119,17 @@ pub const MIN: f32 = f32::MIN; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let min = std::f32::MIN_POSITIVE; /// /// // intended way /// let min = f32::MIN_POSITIVE; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MIN_POSITIVE` associated constant on `f32`" +)] pub const MIN_POSITIVE: f32 = f32::MIN_POSITIVE; /// Largest finite `f32` value. @@ -117,12 +139,14 @@ pub const MIN_POSITIVE: f32 = f32::MIN_POSITIVE; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let max = std::f32::MAX; /// /// // intended way /// let max = f32::MAX; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "TBD", reason = "replaced by the `MAX` associated constant on `f32`")] pub const MAX: f32 = f32::MAX; /// One greater than the minimum possible normal power of 2 exponent. @@ -132,12 +156,17 @@ pub const MAX: f32 = f32::MAX; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let min = std::f32::MIN_EXP; /// /// // intended way /// let min = f32::MIN_EXP; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MIN_EXP` associated constant on `f32`" +)] pub const MIN_EXP: i32 = f32::MIN_EXP; /// Maximum possible power of 2 exponent. @@ -147,12 +176,17 @@ pub const MIN_EXP: i32 = f32::MIN_EXP; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let max = std::f32::MAX_EXP; /// /// // intended way /// let max = f32::MAX_EXP; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MAX_EXP` associated constant on `f32`" +)] pub const MAX_EXP: i32 = f32::MAX_EXP; /// Minimum possible normal power of 10 exponent. @@ -162,12 +196,17 @@ pub const MAX_EXP: i32 = f32::MAX_EXP; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let min = std::f32::MIN_10_EXP; /// /// // intended way /// let min = f32::MIN_10_EXP; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MIN_10_EXP` associated constant on `f32`" +)] pub const MIN_10_EXP: i32 = f32::MIN_10_EXP; /// Maximum possible power of 10 exponent. @@ -177,12 +216,17 @@ pub const MIN_10_EXP: i32 = f32::MIN_10_EXP; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let max = std::f32::MAX_10_EXP; /// /// // intended way /// let max = f32::MAX_10_EXP; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MAX_10_EXP` associated constant on `f32`" +)] pub const MAX_10_EXP: i32 = f32::MAX_10_EXP; /// Not a Number (NaN). @@ -192,12 +236,14 @@ pub const MAX_10_EXP: i32 = f32::MAX_10_EXP; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let nan = std::f32::NAN; /// /// // intended way /// let nan = f32::NAN; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "TBD", reason = "replaced by the `NAN` associated constant on `f32`")] pub const NAN: f32 = f32::NAN; /// Infinity (∞). @@ -207,12 +253,17 @@ pub const NAN: f32 = f32::NAN; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let inf = std::f32::INFINITY; /// /// // intended way /// let inf = f32::INFINITY; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `INFINITY` associated constant on `f32`" +)] pub const INFINITY: f32 = f32::INFINITY; /// Negative infinity (−∞). @@ -222,12 +273,17 @@ pub const INFINITY: f32 = f32::INFINITY; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let ninf = std::f32::NEG_INFINITY; /// /// // intended way /// let ninf = f32::NEG_INFINITY; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `NEG_INFINITY` associated constant on `f32`" +)] pub const NEG_INFINITY: f32 = f32::NEG_INFINITY; /// Basic mathematical constants. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 3323b7d677..7af968f7fe 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1,12 +1,13 @@ -//! This module provides constants which are specific to the implementation -//! of the `f64` floating point data type. +//! Constants specific to the `f64` double-precision floating point type. //! //! *[See also the `f64` primitive type](../../std/primitive.f64.html).* //! //! Mathematically significant numbers are provided in the `consts` sub-module. //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! For the constants defined directly in this module +//! (as distinct from those defined in the `consts` sub-module), +//! new code should instead use the associated constants +//! defined directly on the `f64` type. #![stable(feature = "rust1", since = "1.0.0")] @@ -23,12 +24,14 @@ use crate::num::FpCategory; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let r = std::f64::RADIX; /// /// // intended way /// let r = f64::RADIX; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "TBD", reason = "replaced by the `RADIX` associated constant on `f64`")] pub const RADIX: u32 = f64::RADIX; /// Number of significant digits in base 2. @@ -38,12 +41,17 @@ pub const RADIX: u32 = f64::RADIX; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let d = std::f64::MANTISSA_DIGITS; /// /// // intended way /// let d = f64::MANTISSA_DIGITS; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MANTISSA_DIGITS` associated constant on `f64`" +)] pub const MANTISSA_DIGITS: u32 = f64::MANTISSA_DIGITS; /// Approximate number of significant digits in base 10. @@ -53,12 +61,14 @@ pub const MANTISSA_DIGITS: u32 = f64::MANTISSA_DIGITS; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let d = std::f64::DIGITS; /// /// // intended way /// let d = f64::DIGITS; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "TBD", reason = "replaced by the `DIGITS` associated constant on `f64`")] pub const DIGITS: u32 = f64::DIGITS; /// [Machine epsilon] value for `f64`. @@ -72,12 +82,17 @@ pub const DIGITS: u32 = f64::DIGITS; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let e = std::f64::EPSILON; /// /// // intended way /// let e = f64::EPSILON; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `EPSILON` associated constant on `f64`" +)] pub const EPSILON: f64 = f64::EPSILON; /// Smallest finite `f64` value. @@ -87,12 +102,14 @@ pub const EPSILON: f64 = f64::EPSILON; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let min = std::f64::MIN; /// /// // intended way /// let min = f64::MIN; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "TBD", reason = "replaced by the `MIN` associated constant on `f64`")] pub const MIN: f64 = f64::MIN; /// Smallest positive normal `f64` value. @@ -102,12 +119,17 @@ pub const MIN: f64 = f64::MIN; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let min = std::f64::MIN_POSITIVE; /// /// // intended way /// let min = f64::MIN_POSITIVE; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MIN_POSITIVE` associated constant on `f64`" +)] pub const MIN_POSITIVE: f64 = f64::MIN_POSITIVE; /// Largest finite `f64` value. @@ -117,12 +139,14 @@ pub const MIN_POSITIVE: f64 = f64::MIN_POSITIVE; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let max = std::f64::MAX; /// /// // intended way /// let max = f64::MAX; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "TBD", reason = "replaced by the `MAX` associated constant on `f64`")] pub const MAX: f64 = f64::MAX; /// One greater than the minimum possible normal power of 2 exponent. @@ -132,12 +156,17 @@ pub const MAX: f64 = f64::MAX; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let min = std::f64::MIN_EXP; /// /// // intended way /// let min = f64::MIN_EXP; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MIN_EXP` associated constant on `f64`" +)] pub const MIN_EXP: i32 = f64::MIN_EXP; /// Maximum possible power of 2 exponent. @@ -147,12 +176,17 @@ pub const MIN_EXP: i32 = f64::MIN_EXP; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let max = std::f64::MAX_EXP; /// /// // intended way /// let max = f64::MAX_EXP; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MAX_EXP` associated constant on `f64`" +)] pub const MAX_EXP: i32 = f64::MAX_EXP; /// Minimum possible normal power of 10 exponent. @@ -162,12 +196,17 @@ pub const MAX_EXP: i32 = f64::MAX_EXP; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let min = std::f64::MIN_10_EXP; /// /// // intended way /// let min = f64::MIN_10_EXP; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MIN_10_EXP` associated constant on `f64`" +)] pub const MIN_10_EXP: i32 = f64::MIN_10_EXP; /// Maximum possible power of 10 exponent. @@ -177,12 +216,17 @@ pub const MIN_10_EXP: i32 = f64::MIN_10_EXP; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let max = std::f64::MAX_10_EXP; /// /// // intended way /// let max = f64::MAX_10_EXP; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `MAX_10_EXP` associated constant on `f64`" +)] pub const MAX_10_EXP: i32 = f64::MAX_10_EXP; /// Not a Number (NaN). @@ -192,12 +236,14 @@ pub const MAX_10_EXP: i32 = f64::MAX_10_EXP; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let nan = std::f64::NAN; /// /// // intended way /// let nan = f64::NAN; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated(since = "TBD", reason = "replaced by the `NAN` associated constant on `f64`")] pub const NAN: f64 = f64::NAN; /// Infinity (∞). @@ -207,12 +253,17 @@ pub const NAN: f64 = f64::NAN; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let inf = std::f64::INFINITY; /// /// // intended way /// let inf = f64::INFINITY; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `INFINITY` associated constant on `f64`" +)] pub const INFINITY: f64 = f64::INFINITY; /// Negative infinity (−∞). @@ -222,12 +273,17 @@ pub const INFINITY: f64 = f64::INFINITY; /// /// ```rust /// // deprecated way +/// # #[allow(deprecated, deprecated_in_future)] /// let ninf = std::f64::NEG_INFINITY; /// /// // intended way /// let ninf = f64::NEG_INFINITY; /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_deprecated( + since = "TBD", + reason = "replaced by the `NEG_INFINITY` associated constant on `f64`" +)] pub const NEG_INFINITY: f64 = f64::NEG_INFINITY; /// Basic mathematical constants. diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 2cde5d9995..8fdd7c9e5d 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1,2240 +1,2004 @@ macro_rules! int_impl { - ($SelfT:ty, $ActualT:ident, $UnsignedT:ty, $BITS:expr, $Min:expr, $Max:expr, $Feature:expr, - $EndFeature:expr, $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, + ($SelfT:ty, $ActualT:ident, $UnsignedT:ty, $BITS:expr, $Min:expr, $Max:expr, + $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, $reversed:expr, $le_bytes:expr, $be_bytes:expr, $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { - doc_comment! { - concat!("The smallest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MIN, ", stringify!($Min), ");", -$EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN: Self = !0 ^ ((!0 as $UnsignedT) >> 1) as Self; - } - - doc_comment! { - concat!("The largest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($Max), ");", -$EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX: Self = !Self::MIN; - } - - doc_comment! { - concat!("The size of this integer type in bits. - -# Examples - -``` -", $Feature, "#![feature(int_bits_const)] -assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");", -$EndFeature, " -```"), - #[unstable(feature = "int_bits_const", issue = "76904")] - pub const BITS: u32 = $BITS; - } - - doc_comment! { - concat!("Converts a string slice in a given base to an integer. - -The string is expected to be an optional `+` or `-` sign followed by digits. -Leading and trailing whitespace represent an error. Digits are a subset of these characters, -depending on `radix`: - - * `0-9` - * `a-z` - * `A-Z` - -# Panics - -This function panics if `radix` is not in the range from 2 to 36. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_str_radix(src: &str, radix: u32) -> Result { - from_str_radix(src, radix) - } - } - - doc_comment! { - concat!("Returns the number of ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b100_0000", stringify!($SelfT), "; - -assert_eq!(n.count_ones(), 1);", -$EndFeature, " -``` -"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[doc(alias = "popcount")] - #[doc(alias = "popcnt")] - #[inline] - pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() } - } - - doc_comment! { - concat!("Returns the number of zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 1);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn count_zeros(self) -> u32 { - (!self).count_ones() - } - } - - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = -1", stringify!($SelfT), "; - -assert_eq!(n.leading_zeros(), 0);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn leading_zeros(self) -> u32 { - (self as $UnsignedT).leading_zeros() - } - } - - doc_comment! { - concat!("Returns the number of trailing zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = -4", stringify!($SelfT), "; - -assert_eq!(n.trailing_zeros(), 2);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn trailing_zeros(self) -> u32 { - (self as $UnsignedT).trailing_zeros() - } - } - - doc_comment! { - concat!("Returns the number of leading ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = -1", stringify!($SelfT), "; - -assert_eq!(n.leading_ones(), ", stringify!($BITS), ");", -$EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn leading_ones(self) -> u32 { - (self as $UnsignedT).leading_ones() - } - } - - doc_comment! { - concat!("Returns the number of trailing ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 3", stringify!($SelfT), "; - -assert_eq!(n.trailing_ones(), 2);", -$EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn trailing_ones(self) -> u32 { - (self as $UnsignedT).trailing_ones() - } - } - - doc_comment! { - concat!("Shifts the bits to the left by a specified amount, `n`, -wrapping the truncated bits to the end of the resulting integer. - -Please note this isn't the same operation as the `<<` shifting operator! - -# Examples - -Basic usage: - -``` -let n = ", $rot_op, stringify!($SelfT), "; -let m = ", $rot_result, "; - -assert_eq!(n.rotate_left(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_left(self, n: u32) -> Self { - (self as $UnsignedT).rotate_left(n) as Self - } - } - - doc_comment! { - concat!("Shifts the bits to the right by a specified amount, `n`, -wrapping the truncated bits to the beginning of the resulting -integer. - -Please note this isn't the same operation as the `>>` shifting operator! - -# Examples - -Basic usage: - -``` -let n = ", $rot_result, stringify!($SelfT), "; -let m = ", $rot_op, "; - -assert_eq!(n.rotate_right(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_right(self, n: u32) -> Self { - (self as $UnsignedT).rotate_right(n) as Self - } - } - - doc_comment! { - concat!("Reverses the byte order of the integer. - -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; - -let m = n.swap_bytes(); - -assert_eq!(m, ", $swapped, "); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn swap_bytes(self) -> Self { - (self as $UnsignedT).swap_bytes() as Self - } - } - - doc_comment! { - concat!("Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, - second least-significant bit becomes second most-significant bit, etc. - -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; -let m = n.reverse_bits(); - -assert_eq!(m, ", $reversed, "); -assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits()); -```"), - #[stable(feature = "reverse_bits", since = "1.37.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - #[must_use] - pub const fn reverse_bits(self) -> Self { - (self as $UnsignedT).reverse_bits() as Self - } - } - - doc_comment! { - concat!("Converts an integer from big endian to the target's endianness. - -On big endian this is a no-op. On little endian the bytes are swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(", stringify!($SelfT), "::from_be(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn from_be(x: Self) -> Self { - #[cfg(target_endian = "big")] - { - x - } - #[cfg(not(target_endian = "big"))] - { - x.swap_bytes() - } - } - } - - doc_comment! { - concat!("Converts an integer from little endian to the target's endianness. - -On little endian this is a no-op. On big endian the bytes are swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(", stringify!($SelfT), "::from_le(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn from_le(x: Self) -> Self { - #[cfg(target_endian = "little")] - { - x - } - #[cfg(not(target_endian = "little"))] - { - x.swap_bytes() - } - } - } - - doc_comment! { - concat!("Converts `self` to big endian from the target's endianness. - -On big endian this is a no-op. On little endian the bytes are swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(n.to_be(), n) -} else { - assert_eq!(n.to_be(), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn to_be(self) -> Self { // or not to be? - #[cfg(target_endian = "big")] - { - self - } - #[cfg(not(target_endian = "big"))] - { - self.swap_bytes() - } - } - } - - doc_comment! { - concat!("Converts `self` to little endian from the target's endianness. - -On little endian this is a no-op. On big endian the bytes are swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(n.to_le(), n) -} else { - assert_eq!(n.to_le(), n.swap_bytes()) -}", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] - #[inline] - pub const fn to_le(self) -> Self { - #[cfg(target_endian = "little")] - { - self - } - #[cfg(not(target_endian = "little"))] - { - self.swap_bytes() - } - } - } - - doc_comment! { - concat!("Checked integer addition. Computes `self + rhs`, returning `None` -if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), -"::MAX - 2).checked_add(1), Some(", stringify!($SelfT), "::MAX - 1)); -assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_add(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_add(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer addition. Computes `self + rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), -"::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer subtraction. Computes `self - rhs`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), -"::MIN + 2).checked_sub(1), Some(", stringify!($SelfT), "::MIN + 1)); -assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub(3), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_sub(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_sub(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer subtraction. Computes `self - rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), -"::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer multiplication. Computes `self * rhs`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), -"::MAX.checked_mul(1), Some(", stringify!($SelfT), "::MAX)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_mul(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_mul(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Unchecked integer multiplication. Computes `self * rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), -"::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } - } - } - - doc_comment! { - concat!("Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` -or the division results in overflow. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), -"::MIN + 1).checked_div(-1), Some(", stringify!($Max), ")); -assert_eq!(", stringify!($SelfT), "::MIN.checked_div(-1), None); -assert_eq!((1", stringify!($SelfT), ").checked_div(0), None);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - // SAFETY: div by zero and by INT_MIN have been checked above - Some(unsafe { intrinsics::unchecked_div(self, rhs) }) - } - } - } - - doc_comment! { - concat!("Checked Euclidean division. Computes `self.div_euclid(rhs)`, -returning `None` if `rhs == 0` or the division results in overflow. - -# Examples - -Basic usage: - -``` -assert_eq!((", stringify!($SelfT), -"::MIN + 1).checked_div_euclid(-1), Some(", stringify!($Max), ")); -assert_eq!(", stringify!($SelfT), "::MIN.checked_div_euclid(-1), None); -assert_eq!((1", stringify!($SelfT), ").checked_div_euclid(0), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - Some(self.div_euclid(rhs)) - } - } - } - - doc_comment! { - concat!("Checked integer remainder. Computes `self % rhs`, returning `None` if -`rhs == 0` or the division results in overflow. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None); -assert_eq!(", stringify!($SelfT), "::MIN.checked_rem(-1), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - // SAFETY: div by zero and by INT_MIN have been checked above - Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) - } - } - } - - doc_comment! { - concat!("Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` -if `rhs == 0` or the division results in overflow. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None); -assert_eq!(", stringify!($SelfT), "::MIN.checked_rem_euclid(-1), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { - None - } else { - Some(self.rem_euclid(rhs)) - } - } - } - - doc_comment! { - concat!("Checked negation. Computes `-self`, returning `None` if `self == MIN`. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".checked_neg(), Some(-5)); -assert_eq!(", stringify!($SelfT), "::MIN.checked_neg(), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[inline] - pub const fn checked_neg(self) -> Option { - let (a, b) = self.overflowing_neg(); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger -than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10)); -assert_eq!(0x1", stringify!($SelfT), ".checked_shl(129), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shl(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is -larger than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1)); -assert_eq!(0x10", stringify!($SelfT), ".checked_shr(128), None);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shr(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) {None} else {Some(a)} - } - } - - doc_comment! { - concat!("Checked absolute value. Computes `self.abs()`, returning `None` if -`self == MIN`. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!((-5", stringify!($SelfT), ").checked_abs(), Some(5)); -assert_eq!(", stringify!($SelfT), "::MIN.checked_abs(), None);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_abs", since = "1.13.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[inline] - pub const fn checked_abs(self) -> Option { - if self.is_negative() { - self.checked_neg() - } else { - Some(self) - } - } - } - - doc_comment! { - concat!("Checked exponentiation. Computes `self.pow(exp)`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(8", stringify!($SelfT), ".checked_pow(2), Some(64)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);", -$EndFeature, " -```"), - - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_pow(self, mut exp: u32) -> Option { - if exp == 0 { - return Some(1); - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = try_opt!(acc.checked_mul(base)); - } - exp /= 2; - base = try_opt!(base.checked_mul(base)); - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - Some(try_opt!(acc.checked_mul(base))) - } - } - - doc_comment! { - concat!("Saturating integer addition. Computes `self + rhs`, saturating at the numeric -bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(100), ", stringify!($SelfT), -"::MAX); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_add(-1), ", stringify!($SelfT), -"::MIN);", -$EndFeature, " -```"), - - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_add(self, rhs: Self) -> Self { - intrinsics::saturating_add(self, rhs) - } - } - - doc_comment! { - concat!("Saturating integer subtraction. Computes `self - rhs`, saturating at the -numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_sub(100), ", stringify!($SelfT), -"::MIN); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_sub(-1), ", stringify!($SelfT), -"::MAX);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_sub(self, rhs: Self) -> Self { - intrinsics::saturating_sub(self, rhs) - } - } - - doc_comment! { - concat!("Saturating integer negation. Computes `-self`, returning `MAX` if `self == MIN` -instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_neg(), -100); -assert_eq!((-100", stringify!($SelfT), ").saturating_neg(), 100); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_neg(), ", stringify!($SelfT), -"::MAX); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_neg(), ", stringify!($SelfT), -"::MIN + 1);", -$EndFeature, " -```"), - - #[stable(feature = "saturating_neg", since = "1.45.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] - pub const fn saturating_neg(self) -> Self { - intrinsics::saturating_sub(0, self) - } - } - - doc_comment! { - concat!("Saturating absolute value. Computes `self.abs()`, returning `MAX` if `self == -MIN` instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_abs(), 100); -assert_eq!((-100", stringify!($SelfT), ").saturating_abs(), 100); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_abs(), ", stringify!($SelfT), -"::MAX); -assert_eq!((", stringify!($SelfT), "::MIN + 1).saturating_abs(), ", stringify!($SelfT), -"::MAX);", -$EndFeature, " -```"), - - #[stable(feature = "saturating_neg", since = "1.45.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] - pub const fn saturating_abs(self) -> Self { - if self.is_negative() { - self.saturating_neg() - } else { - self - } - } - } - - doc_comment! { - concat!("Saturating integer multiplication. Computes `self * rhs`, saturating at the -numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(10", stringify!($SelfT), ".saturating_mul(12), 120); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_mul(10), ", stringify!($SelfT), "::MAX); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_mul(10), ", stringify!($SelfT), "::MIN);", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_mul(self, rhs: Self) -> Self { - match self.checked_mul(rhs) { - Some(x) => x, - None => if (self < 0) == (rhs < 0) { - Self::MAX - } else { - Self::MIN - } - } - } - } - - doc_comment! { - concat!("Saturating integer exponentiation. Computes `self.pow(exp)`, -saturating at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!((-4", stringify!($SelfT), ").saturating_pow(3), -64); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(2), ", stringify!($SelfT), "::MAX); -assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(3), ", stringify!($SelfT), "::MIN);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Some(x) => x, - None if self < 0 && exp % 2 == 1 => Self::MIN, - None => Self::MAX, - } - } - } - - doc_comment! { - concat!("Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the -boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_add(27), 127); -assert_eq!(", stringify!($SelfT), "::MAX.wrapping_add(2), ", stringify!($SelfT), -"::MIN + 1);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_add(self, rhs: Self) -> Self { - intrinsics::wrapping_add(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the -boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0", stringify!($SelfT), ".wrapping_sub(127), -127); -assert_eq!((-2", stringify!($SelfT), ").wrapping_sub(", stringify!($SelfT), "::MAX), ", -stringify!($SelfT), "::MAX);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_sub(self, rhs: Self) -> Self { - intrinsics::wrapping_sub(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at -the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".wrapping_mul(12), 120); -assert_eq!(11i8.wrapping_mul(12), -124);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_mul(self, rhs: Self) -> Self { - intrinsics::wrapping_mul(self, rhs) - } - } - - doc_comment! { - concat!("Wrapping (modular) division. Computes `self / rhs`, wrapping around at the -boundary of the type. - -The only case where such wrapping can occur is when one divides `MIN / -1` on a signed type (where -`MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value -that is too large to represent in the type. In such a case, this function returns `MIN` itself. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10); -assert_eq!((-128i8).wrapping_div(-1), -128);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div(self, rhs: Self) -> Self { - self.overflowing_div(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping Euclidean division. Computes `self.div_euclid(rhs)`, -wrapping around at the boundary of the type. - -Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is the negative minimal value -for the type). This is equivalent to `-MIN`, a positive value that is too large to represent in the -type. In this case, this method returns `MIN` itself. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10); -assert_eq!((-128i8).wrapping_div_euclid(-1), -128); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { - self.overflowing_div_euclid(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at the -boundary of the type. - -Such wrap-around never actually occurs mathematically; implementation artifacts make `x % y` -invalid for `MIN / -1` on a signed type (where `MIN` is the negative minimal value). In such a case, -this function returns `0`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0); -assert_eq!((-128i8).wrapping_rem(-1), 0);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem(self, rhs: Self) -> Self { - self.overflowing_rem(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping around -at the boundary of the type. - -Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is the negative minimal value -for the type). In this case, this method returns 0. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0); -assert_eq!((-128i8).wrapping_rem_euclid(-1), 0); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { - self.overflowing_rem_euclid(rhs).0 - } - } - - doc_comment! { - concat!("Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary -of the type. - -The only case where such wrapping can occur is when one negates `MIN` on a signed type (where `MIN` -is the negative minimal value for the type); this is a positive value that is too large to represent -in the type. In such a case, this function returns `MIN` itself. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_neg(), -100); -assert_eq!(", stringify!($SelfT), "::MIN.wrapping_neg(), ", stringify!($SelfT), -"::MIN);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn wrapping_neg(self) -> Self { - self.overflowing_neg().0 - } - } - - doc_comment! { - concat!("Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes -any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to -the range of the type, rather than the bits shifted out of the LHS being returned to the other end. -The primitive integer types all implement a [`rotate_left`](#method.rotate_left) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(7), -128); -assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(128), -1);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shl(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) - } - } - } - - doc_comment! { - concat!("Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` -removes any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted -to the range of the type, rather than the bits shifted out of the LHS being returned to the other -end. The primitive integer types all implement a [`rotate_right`](#method.rotate_right) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((-128", stringify!($SelfT), ").wrapping_shr(7), -1); -assert_eq!((-128i16).wrapping_shr(64), -128);", -$EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shr(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) - } - } - } - - doc_comment! { - concat!("Wrapping (modular) absolute value. Computes `self.abs()`, wrapping around at -the boundary of the type. - -The only case where such wrapping can occur is when one takes the absolute value of the negative -minimal value for the type; this is a positive value that is too large to represent in the type. In -such a case, this function returns `MIN` itself. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_abs(), 100); -assert_eq!((-100", stringify!($SelfT), ").wrapping_abs(), 100); -assert_eq!(", stringify!($SelfT), "::MIN.wrapping_abs(), ", stringify!($SelfT), -"::MIN); -assert_eq!((-128i8).wrapping_abs() as u8, 128);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_abs", since = "1.13.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[allow(unused_attributes)] - #[inline] - pub const fn wrapping_abs(self) -> Self { - if self.is_negative() { - self.wrapping_neg() - } else { - self - } - } - } - - doc_comment! { - concat!("Computes the absolute value of `self` without any wrapping -or panicking. - - -# Examples - -Basic usage: - -``` -", $Feature, "#![feature(unsigned_abs)] -assert_eq!(100", stringify!($SelfT), ".unsigned_abs(), 100", stringify!($UnsignedT), "); -assert_eq!((-100", stringify!($SelfT), ").unsigned_abs(), 100", stringify!($UnsignedT), "); -assert_eq!((-128i8).unsigned_abs(), 128u8);", -$EndFeature, " -```"), - #[unstable(feature = "unsigned_abs", issue = "74913")] - #[inline] - pub const fn unsigned_abs(self) -> $UnsignedT { - self.wrapping_abs() as $UnsignedT - } - } - - doc_comment! { - concat!("Wrapping (modular) exponentiation. Computes `self.pow(exp)`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".wrapping_pow(4), 81); -assert_eq!(3i8.wrapping_pow(5), -13); -assert_eq!(3i8.wrapping_pow(6), -39);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); - } - exp /= 2; - base = base.wrapping_mul(base); - } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) - } - } - - doc_comment! { - concat!("Calculates `self` + `rhs` - -Returns a tuple of the addition along with a boolean indicating whether an arithmetic overflow would -occur. If an overflow would have occurred then the wrapped value is returned. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false)); -assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (", stringify!($SelfT), -"::MIN, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - doc_comment! { - concat!("Calculates `self` - `rhs` - -Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic overflow -would occur. If an overflow would have occurred then the wrapped value is returned. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_sub(1), (", stringify!($SelfT), -"::MAX, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - doc_comment! { - concat!("Calculates the multiplication of `self` and `rhs`. - -Returns a tuple of the multiplication along with a boolean indicating whether an arithmetic overflow -would occur. If an overflow would have occurred then the wrapped value is returned. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_mul(2), (10, false)); -assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } - } - - doc_comment! { - concat!("Calculates the divisor when `self` is divided by `rhs`. - -Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would -occur. If an overflow would occur then self is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div(-1), (", stringify!($SelfT), -"::MIN, true));", -$EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (self, true) - } else { - (self / rhs, false) - } - } - } - - doc_comment! { - concat!("Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. - -Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would -occur. If an overflow would occur then `self` is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div_euclid(-1), (", stringify!($SelfT), -"::MIN, true)); -```"), - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (self, true) - } else { - (self.div_euclid(rhs), false) - } - } - } - - doc_comment! { - concat!("Calculates the remainder when `self` is divided by `rhs`. - -Returns a tuple of the remainder after dividing along with a boolean indicating whether an -arithmetic overflow would occur. If an overflow would occur then 0 is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem(-1), (0, true));", -$EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (0, true) - } else { - (self % rhs, false) - } - } - } - - - doc_comment! { - concat!("Overflowing Euclidean remainder. Calculates `self.rem_euclid(rhs)`. - -Returns a tuple of the remainder after dividing along with a boolean indicating whether an -arithmetic overflow would occur. If an overflow would occur then 0 is returned. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem_euclid(-1), (0, true)); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { - if unlikely!(self == Self::MIN && rhs == -1) { - (0, true) - } else { - (self.rem_euclid(rhs), false) + /// The smallest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN, ", stringify!($Min), ");")] + /// ``` + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN: Self = !0 ^ ((!0 as $UnsignedT) >> 1) as Self; + + /// The largest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($Max), ");")] + /// ``` + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX: Self = !Self::MIN; + + /// The size of this integer type in bits. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_bits_const)] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");")] + /// ``` + #[unstable(feature = "int_bits_const", issue = "76904")] + pub const BITS: u32 = $BITS; + + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` or `-` sign followed by digits. + /// Leading and trailing whitespace represent an error. Digits are a subset of these characters, + /// depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_str_radix(src: &str, radix: u32) -> Result { + from_str_radix(src, radix) + } + + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0b100_0000", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.count_ones(), 1); + /// ``` + /// + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[doc(alias = "popcount")] + #[doc(alias = "popcnt")] + #[inline] + pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() } + + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 1);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn count_zeros(self) -> u32 { + (!self).count_ones() + } + + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = -1", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.leading_zeros(), 0); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn leading_zeros(self) -> u32 { + (self as $UnsignedT).leading_zeros() + } + + /// Returns the number of trailing zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = -4", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.trailing_zeros(), 2); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn trailing_zeros(self) -> u32 { + (self as $UnsignedT).trailing_zeros() + } + + /// Returns the number of leading ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = -1", stringify!($SelfT), ";")] + /// + #[doc = concat!("assert_eq!(n.leading_ones(), ", stringify!($BITS), ");")] + /// ``` + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn leading_ones(self) -> u32 { + (self as $UnsignedT).leading_ones() + } + + /// Returns the number of trailing ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 3", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.trailing_ones(), 2); + /// ``` + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn trailing_ones(self) -> u32 { + (self as $UnsignedT).trailing_ones() + } + + /// Shifts the bits to the left by a specified amount, `n`, + /// wrapping the truncated bits to the end of the resulting integer. + /// + /// Please note this isn't the same operation as the `<<` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $rot_op, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $rot_result, ";")] + /// + #[doc = concat!("assert_eq!(n.rotate_left(", $rot, "), m);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_left(self, n: u32) -> Self { + (self as $UnsignedT).rotate_left(n) as Self + } + + /// Shifts the bits to the right by a specified amount, `n`, + /// wrapping the truncated bits to the beginning of the resulting + /// integer. + /// + /// Please note this isn't the same operation as the `>>` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $rot_result, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $rot_op, ";")] + /// + #[doc = concat!("assert_eq!(n.rotate_right(", $rot, "), m);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_right(self, n: u32) -> Self { + (self as $UnsignedT).rotate_right(n) as Self + } + + /// Reverses the byte order of the integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")] + /// + /// let m = n.swap_bytes(); + /// + #[doc = concat!("assert_eq!(m, ", $swapped, ");")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn swap_bytes(self) -> Self { + (self as $UnsignedT).swap_bytes() as Self + } + + /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, + /// second least-significant bit becomes second most-significant bit, etc. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")] + /// let m = n.reverse_bits(); + /// + #[doc = concat!("assert_eq!(m, ", $reversed, ");")] + #[doc = concat!("assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits());")] + /// ``` + #[stable(feature = "reverse_bits", since = "1.37.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + #[must_use] + pub const fn reverse_bits(self) -> Self { + (self as $UnsignedT).reverse_bits() as Self + } + + /// Converts an integer from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "big") { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_be(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes())")] + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn from_be(x: Self) -> Self { + #[cfg(target_endian = "big")] + { + x + } + #[cfg(not(target_endian = "big"))] + { + x.swap_bytes() + } + } + + /// Converts an integer from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "little") { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_le(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes())")] + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn from_le(x: Self) -> Self { + #[cfg(target_endian = "little")] + { + x + } + #[cfg(not(target_endian = "little"))] + { + x.swap_bytes() + } + } + + /// Converts `self` to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_be(), n) + /// } else { + /// assert_eq!(n.to_be(), n.swap_bytes()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn to_be(self) -> Self { // or not to be? + #[cfg(target_endian = "big")] + { + self + } + #[cfg(not(target_endian = "big"))] + { + self.swap_bytes() + } + } + + /// Converts `self` to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_le(), n) + /// } else { + /// assert_eq!(n.to_le(), n.swap_bytes()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_conversions", since = "1.32.0")] + #[inline] + pub const fn to_le(self) -> Self { + #[cfg(target_endian = "little")] + { + self + } + #[cfg(not(target_endian = "little"))] + { + self.swap_bytes() + } + } + + /// Checked integer addition. Computes `self + rhs`, returning `None` + /// if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(1), Some(", stringify!($SelfT), "::MAX - 1));")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_add(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_add(rhs); + if unlikely!(b) {None} else {Some(a)} + } + + /// Unchecked integer addition. Computes `self + rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self + rhs > ", stringify!($SelfT), "::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_add`. + unsafe { intrinsics::unchecked_add(self, rhs) } + } + + /// Checked integer subtraction. Computes `self - rhs`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub(1), Some(", stringify!($SelfT), "::MIN + 1));")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub(3), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_sub(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_sub(rhs); + if unlikely!(b) {None} else {Some(a)} + } + + /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self - rhs > ", stringify!($SelfT), "::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_sub`. + unsafe { intrinsics::unchecked_sub(self, rhs) } + } + + /// Checked integer multiplication. Computes `self * rhs`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(1), Some(", stringify!($SelfT), "::MAX));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_mul(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_mul(rhs); + if unlikely!(b) {None} else {Some(a)} + } + + /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self * rhs > ", stringify!($SelfT), "::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_mul`. + unsafe { intrinsics::unchecked_mul(self, rhs) } + } + + /// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` + /// or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_div(-1), Some(", stringify!($Max), "));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_div(-1), None);")] + #[doc = concat!("assert_eq!((1", stringify!($SelfT), ").checked_div(0), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + // SAFETY: div by zero and by INT_MIN have been checked above + Some(unsafe { intrinsics::unchecked_div(self, rhs) }) + } + } + + /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, + /// returning `None` if `rhs == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_div_euclid(-1), Some(", stringify!($Max), "));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_div_euclid(-1), None);")] + #[doc = concat!("assert_eq!((1", stringify!($SelfT), ").checked_div_euclid(0), None);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + Some(self.div_euclid(rhs)) + } + } + + /// Checked integer remainder. Computes `self % rhs`, returning `None` if + /// `rhs == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1));")] + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_rem(-1), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + // SAFETY: div by zero and by INT_MIN have been checked above + Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) + } + } + + /// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` + /// if `rhs == 0` or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1));")] + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_rem_euclid(-1), None);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0 || (self == Self::MIN && rhs == -1)) { + None + } else { + Some(self.rem_euclid(rhs)) + } + } + + /// Checked negation. Computes `-self`, returning `None` if `self == MIN`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_neg(), Some(-5));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_neg(), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[inline] + pub const fn checked_neg(self) -> Option { + let (a, b) = self.overflowing_neg(); + if unlikely!(b) {None} else {Some(a)} + } + + /// Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger + /// than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10));")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(129), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shl(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shl(rhs); + if unlikely!(b) {None} else {Some(a)} + } + + /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is + /// larger than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(128), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shr(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shr(rhs); + if unlikely!(b) {None} else {Some(a)} + } + + /// Checked absolute value. Computes `self.abs()`, returning `None` if + /// `self == MIN`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!((-5", stringify!($SelfT), ").checked_abs(), Some(5));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_abs(), None);")] + /// ``` + #[stable(feature = "no_panic_abs", since = "1.13.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[inline] + pub const fn checked_abs(self) -> Option { + if self.is_negative() { + self.checked_neg() + } else { + Some(self) + } + } + + /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(8", stringify!($SelfT), ".checked_pow(2), Some(64));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);")] + /// ``` + + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_pow(self, mut exp: u32) -> Option { + if exp == 0 { + return Some(1); + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = try_opt!(acc.checked_mul(base)); } - } - } - - - doc_comment! { - concat!("Negates self, overflowing if this is equal to the minimum value. - -Returns a tuple of the negated version of self along with a boolean indicating whether an overflow -happened. If `self` is the minimum value (e.g., `i32::MIN` for values of type `i32`), then the -minimum value will be returned again and `true` will be returned for an overflow happening. - -# Examples - -Basic usage: - -``` -assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2, false)); -assert_eq!(", stringify!($SelfT), "::MIN.overflowing_neg(), (", stringify!($SelfT), -"::MIN, true));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[allow(unused_attributes)] - pub const fn overflowing_neg(self) -> (Self, bool) { - if unlikely!(self == Self::MIN) { - (Self::MIN, true) + exp /= 2; + base = try_opt!(base.checked_mul(base)); + } + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + Some(try_opt!(acc.checked_mul(base))) + } + + /// Saturating integer addition. Computes `self + rhs`, saturating at the numeric + /// bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(100), ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_add(-1), ", stringify!($SelfT), "::MIN);")] + /// ``` + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_add(self, rhs: Self) -> Self { + intrinsics::saturating_add(self, rhs) + } + + /// Saturating integer subtraction. Computes `self - rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_sub(100), ", stringify!($SelfT), "::MIN);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_sub(-1), ", stringify!($SelfT), "::MAX);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_sub(self, rhs: Self) -> Self { + intrinsics::saturating_sub(self, rhs) + } + + /// Saturating integer negation. Computes `-self`, returning `MAX` if `self == MIN` + /// instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_neg(), -100);")] + #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").saturating_neg(), 100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_neg(), ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_neg(), ", stringify!($SelfT), "::MIN + 1);")] + /// ``` + + #[stable(feature = "saturating_neg", since = "1.45.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_neg(self) -> Self { + intrinsics::saturating_sub(0, self) + } + + /// Saturating absolute value. Computes `self.abs()`, returning `MAX` if `self == + /// MIN` instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_abs(), 100);")] + #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").saturating_abs(), 100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_abs(), ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).saturating_abs(), ", stringify!($SelfT), "::MAX);")] + /// ``` + + #[stable(feature = "saturating_neg", since = "1.45.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_abs(self) -> Self { + if self.is_negative() { + self.saturating_neg() + } else { + self + } + } + + /// Saturating integer multiplication. Computes `self * rhs`, saturating at the + /// numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".saturating_mul(12), 120);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_mul(10), ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_mul(10), ", stringify!($SelfT), "::MIN);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_mul(self, rhs: Self) -> Self { + match self.checked_mul(rhs) { + Some(x) => x, + None => if (self < 0) == (rhs < 0) { + Self::MAX } else { - (-self, false) + Self::MIN } } } - doc_comment! { - concat!("Shifts self left by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean indicating whether the shift -value was larger than or equal to the number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then used to perform the shift. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT),".overflowing_shl(4), (0x10, false)); -assert_eq!(0x1i32.overflowing_shl(36), (0x10, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) - } - } - - doc_comment! { - concat!("Shifts self right by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean indicating whether the shift -value was larger than or equal to the number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then used to perform the shift. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false)); -assert_eq!(0x10i32.overflowing_shr(36), (0x1, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) - } - } - - doc_comment! { - concat!("Computes the absolute value of `self`. - -Returns a tuple of the absolute version of self along with a boolean indicating whether an overflow -happened. If self is the minimum value (e.g., ", stringify!($SelfT), "::MIN for values of type - ", stringify!($SelfT), "), then the minimum value will be returned again and true will be returned -for an overflow happening. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".overflowing_abs(), (10, false)); -assert_eq!((-10", stringify!($SelfT), ").overflowing_abs(), (10, false)); -assert_eq!((", stringify!($SelfT), "::MIN).overflowing_abs(), (", stringify!($SelfT), -"::MIN, true));", -$EndFeature, " -```"), - #[stable(feature = "no_panic_abs", since = "1.13.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn overflowing_abs(self) -> (Self, bool) { - (self.wrapping_abs(), self == Self::MIN) - } - } - - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -Returns a tuple of the exponentiation along with a bool indicating -whether an overflow happened. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".overflowing_pow(4), (81, false)); -assert_eq!(3i8.overflowing_pow(5), (-13, true));", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { - if exp == 0 { - return (1,false); + /// Saturating integer exponentiation. Computes `self.pow(exp)`, + /// saturating at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!((-4", stringify!($SelfT), ").saturating_pow(3), -64);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(2), ", stringify!($SelfT), "::MAX);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(3), ", stringify!($SelfT), "::MIN);")] + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Some(x) => x, + None if self < 0 && exp % 2 == 1 => Self::MIN, + None => Self::MAX, + } + } + + /// Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the + /// boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_add(27), 127);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_add(2), ", stringify!($SelfT), "::MIN + 1);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_add(self, rhs: Self) -> Self { + intrinsics::wrapping_add(self, rhs) + } + + /// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the + /// boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".wrapping_sub(127), -127);")] + #[doc = concat!("assert_eq!((-2", stringify!($SelfT), ").wrapping_sub(", stringify!($SelfT), "::MAX), ", stringify!($SelfT), "::MAX);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_sub(self, rhs: Self) -> Self { + intrinsics::wrapping_sub(self, rhs) + } + + /// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at + /// the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".wrapping_mul(12), 120);")] + /// assert_eq!(11i8.wrapping_mul(12), -124); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_mul(self, rhs: Self) -> Self { + intrinsics::wrapping_mul(self, rhs) + } + + /// Wrapping (modular) division. Computes `self / rhs`, wrapping around at the + /// boundary of the type. + /// + /// The only case where such wrapping can occur is when one divides `MIN / -1` on a signed type (where + /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// that is too large to represent in the type. In such a case, this function returns `MIN` itself. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10);")] + /// assert_eq!((-128i8).wrapping_div(-1), -128); + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div(self, rhs: Self) -> Self { + self.overflowing_div(rhs).0 + } + + /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`, + /// wrapping around at the boundary of the type. + /// + /// Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is the negative minimal value + /// for the type). This is equivalent to `-MIN`, a positive value that is too large to represent in the + /// type. In this case, this method returns `MIN` itself. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10);")] + /// assert_eq!((-128i8).wrapping_div_euclid(-1), -128); + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { + self.overflowing_div_euclid(rhs).0 + } + + /// Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at the + /// boundary of the type. + /// + /// Such wrap-around never actually occurs mathematically; implementation artifacts make `x % y` + /// invalid for `MIN / -1` on a signed type (where `MIN` is the negative minimal value). In such a case, + /// this function returns `0`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0);")] + /// assert_eq!((-128i8).wrapping_rem(-1), 0); + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem(self, rhs: Self) -> Self { + self.overflowing_rem(rhs).0 + } + + /// Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping around + /// at the boundary of the type. + /// + /// Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is the negative minimal value + /// for the type). In this case, this method returns 0. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0);")] + /// assert_eq!((-128i8).wrapping_rem_euclid(-1), 0); + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { + self.overflowing_rem_euclid(rhs).0 + } + + /// Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary + /// of the type. + /// + /// The only case where such wrapping can occur is when one negates `MIN` on a signed type (where `MIN` + /// is the negative minimal value for the type); this is a positive value that is too large to represent + /// in the type. In such a case, this function returns `MIN` itself. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_neg(), -100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.wrapping_neg(), ", stringify!($SelfT), "::MIN);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn wrapping_neg(self) -> Self { + self.overflowing_neg().0 + } + + /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes + /// any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to + /// the range of the type, rather than the bits shifted out of the LHS being returned to the other end. + /// The primitive integer types all implement a [`rotate_left`](#method.rotate_left) function, + /// which may be what you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(7), -128);")] + #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(128), -1);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shl(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) + } + } + + /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` + /// removes any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted + /// to the range of the type, rather than the bits shifted out of the LHS being returned to the other + /// end. The primitive integer types all implement a [`rotate_right`](#method.rotate_right) function, + /// which may be what you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!((-128", stringify!($SelfT), ").wrapping_shr(7), -1);")] + /// assert_eq!((-128i16).wrapping_shr(64), -128); + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shr(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) + } + } + + /// Wrapping (modular) absolute value. Computes `self.abs()`, wrapping around at + /// the boundary of the type. + /// + /// The only case where such wrapping can occur is when one takes the absolute value of the negative + /// minimal value for the type; this is a positive value that is too large to represent in the type. In + /// such a case, this function returns `MIN` itself. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_abs(), 100);")] + #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").wrapping_abs(), 100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.wrapping_abs(), ", stringify!($SelfT), "::MIN);")] + /// assert_eq!((-128i8).wrapping_abs() as u8, 128); + /// ``` + #[stable(feature = "no_panic_abs", since = "1.13.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[allow(unused_attributes)] + #[inline] + pub const fn wrapping_abs(self) -> Self { + if self.is_negative() { + self.wrapping_neg() + } else { + self + } + } + + /// Computes the absolute value of `self` without any wrapping + /// or panicking. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".unsigned_abs(), 100", stringify!($UnsignedT), ");")] + #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").unsigned_abs(), 100", stringify!($UnsignedT), ");")] + /// assert_eq!((-128i8).unsigned_abs(), 128u8); + /// ``` + #[stable(feature = "unsigned_abs", since = "1.51.0")] + #[rustc_const_stable(feature = "unsigned_abs", since = "1.51.0")] + #[inline] + pub const fn unsigned_abs(self) -> $UnsignedT { + self.wrapping_abs() as $UnsignedT + } + + /// Wrapping (modular) exponentiation. Computes `self.pow(exp)`, + /// wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_pow(4), 81);")] + /// assert_eq!(3i8.wrapping_pow(5), -13); + /// assert_eq!(3i8.wrapping_pow(6), -39); + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc: Self = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); } - let mut base = self; - let mut acc: Self = 1; - let mut overflown = false; - // Scratch space for storing results of overflowing_mul. - let mut r; - - while exp > 1 { - if (exp & 1) == 1 { - r = acc.overflowing_mul(base); - acc = r.0; - overflown |= r.1; - } - exp /= 2; - r = base.overflowing_mul(base); - base = r.0; + exp /= 2; + base = base.wrapping_mul(base); + } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.wrapping_mul(base) + } + + /// Calculates `self` + `rhs` + /// + /// Returns a tuple of the addition along with a boolean indicating whether an arithmetic overflow would + /// occur. If an overflow would have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (", stringify!($SelfT), "::MIN, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + + /// Calculates `self` - `rhs` + /// + /// Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic overflow + /// would occur. If an overflow would have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_sub(1), (", stringify!($SelfT), "::MAX, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + + /// Calculates the multiplication of `self` and `rhs`. + /// + /// Returns a tuple of the multiplication along with a boolean indicating whether an arithmetic overflow + /// would occur. If an overflow would have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_mul(2), (10, false));")] + /// assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true)); + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } + + /// Calculates the divisor when `self` is divided by `rhs`. + /// + /// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would + /// occur. If an overflow would occur then self is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div(-1), (", stringify!($SelfT), "::MIN, true));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (self, true) + } else { + (self / rhs, false) + } + } + + /// Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. + /// + /// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would + /// occur. If an overflow would occur then `self` is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div_euclid(-1), (", stringify!($SelfT), "::MIN, true));")] + /// ``` + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (self, true) + } else { + (self.div_euclid(rhs), false) + } + } + + /// Calculates the remainder when `self` is divided by `rhs`. + /// + /// Returns a tuple of the remainder after dividing along with a boolean indicating whether an + /// arithmetic overflow would occur. If an overflow would occur then 0 is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem(-1), (0, true));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (0, true) + } else { + (self % rhs, false) + } + } + + + /// Overflowing Euclidean remainder. Calculates `self.rem_euclid(rhs)`. + /// + /// Returns a tuple of the remainder after dividing along with a boolean indicating whether an + /// arithmetic overflow would occur. If an overflow would occur then 0 is returned. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem_euclid(-1), (0, true));")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { + if unlikely!(self == Self::MIN && rhs == -1) { + (0, true) + } else { + (self.rem_euclid(rhs), false) + } + } + + + /// Negates self, overflowing if this is equal to the minimum value. + /// + /// Returns a tuple of the negated version of self along with a boolean indicating whether an overflow + /// happened. If `self` is the minimum value (e.g., `i32::MIN` for values of type `i32`), then the + /// minimum value will be returned again and `true` will be returned for an overflow happening. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_neg(), (", stringify!($SelfT), "::MIN, true));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[allow(unused_attributes)] + pub const fn overflowing_neg(self) -> (Self, bool) { + if unlikely!(self == Self::MIN) { + (Self::MIN, true) + } else { + (-self, false) + } + } + + /// Shifts self left by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean indicating whether the shift + /// value was larger than or equal to the number of bits. If the shift value is too large, then value is + /// masked (N-1) where N is the number of bits, and this value is then used to perform the shift. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT),".overflowing_shl(4), (0x10, false));")] + /// assert_eq!(0x1i32.overflowing_shl(36), (0x10, true)); + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) + } + + /// Shifts self right by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean indicating whether the shift + /// value was larger than or equal to the number of bits. If the shift value is too large, then value is + /// masked (N-1) where N is the number of bits, and this value is then used to perform the shift. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false));")] + /// assert_eq!(0x10i32.overflowing_shr(36), (0x1, true)); + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) + } + + /// Computes the absolute value of `self`. + /// + /// Returns a tuple of the absolute version of self along with a boolean indicating whether an overflow + /// happened. If self is the minimum value + #[doc = concat!("(e.g., ", stringify!($SelfT), "::MIN for values of type ", stringify!($SelfT), "),")] + /// then the minimum value will be returned again and true will be returned + /// for an overflow happening. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".overflowing_abs(), (10, false));")] + #[doc = concat!("assert_eq!((-10", stringify!($SelfT), ").overflowing_abs(), (10, false));")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN).overflowing_abs(), (", stringify!($SelfT), "::MIN, true));")] + /// ``` + #[stable(feature = "no_panic_abs", since = "1.13.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn overflowing_abs(self) -> (Self, bool) { + (self.wrapping_abs(), self == Self::MIN) + } + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// Returns a tuple of the exponentiation along with a bool indicating + /// whether an overflow happened. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".overflowing_pow(4), (81, false));")] + /// assert_eq!(3i8.overflowing_pow(5), (-13, true)); + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { + if exp == 0 { + return (1,false); + } + let mut base = self; + let mut acc: Self = 1; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; + + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; overflown |= r.1; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; - r - } - } - - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -# Examples - -Basic usage: - -``` -", $Feature, "let x: ", stringify!($SelfT), " = 2; // or any other integer type - -assert_eq!(x.pow(5), 32);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc * base; - } - exp /= 2; - base = base * base; - } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc * base - } - } - - doc_comment! { - concat!("Calculates the quotient of Euclidean division of `self` by `rhs`. - -This computes the integer `n` such that `self = n * rhs + self.rem_euclid(rhs)`, -with `0 <= self.rem_euclid(rhs) < rhs`. - -In other words, the result is `self / rhs` rounded to the integer `n` -such that `self >= n * rhs`. -If `self > 0`, this is equal to round towards zero (the default in Rust); -if `self < 0`, this is equal to round towards +/- infinity. - -# Panics - -This function will panic if `rhs` is 0 or the division results in overflow. - -# Examples - -Basic usage: - -``` -let a: ", stringify!($SelfT), " = 7; // or any other integer type -let b = 4; - -assert_eq!(a.div_euclid(b), 1); // 7 >= 4 * 1 -assert_eq!(a.div_euclid(-b), -1); // 7 >= -4 * -1 -assert_eq!((-a).div_euclid(b), -2); // -7 >= 4 * -2 -assert_eq!((-a).div_euclid(-b), 2); // -7 >= -4 * 2 -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn div_euclid(self, rhs: Self) -> Self { - let q = self / rhs; - if self % rhs < 0 { - return if rhs > 0 { q - 1 } else { q + 1 } - } - q - } - } - - - doc_comment! { - concat!("Calculates the least nonnegative remainder of `self (mod rhs)`. - -This is done as if by the Euclidean division algorithm -- given -`r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r`, and -`0 <= r < abs(rhs)`. - -# Panics - -This function will panic if `rhs` is 0 or the division results in overflow. - -# Examples - -Basic usage: - -``` -let a: ", stringify!($SelfT), " = 7; // or any other integer type -let b = 4; - -assert_eq!(a.rem_euclid(b), 3); -assert_eq!((-a).rem_euclid(b), 1); -assert_eq!(a.rem_euclid(-b), 3); -assert_eq!((-a).rem_euclid(-b), 1); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn rem_euclid(self, rhs: Self) -> Self { - let r = self % rhs; - if r < 0 { - if rhs < 0 { - r - rhs - } else { - r + rhs - } - } else { - r + exp /= 2; + r = base.overflowing_mul(base); + base = r.0; + overflown |= r.1; + } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + r = acc.overflowing_mul(base); + r.1 |= overflown; + r + } + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let x: ", stringify!($SelfT), " = 2; // or any other integer type")] + /// + /// assert_eq!(x.pow(5), 32); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc = 1; + + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * base; } - } - } - - doc_comment! { - concat!("Computes the absolute value of `self`. - -# Overflow behavior - -The absolute value of `", stringify!($SelfT), "::MIN` cannot be represented as an -`", stringify!($SelfT), "`, and attempting to calculate it will cause an overflow. This means that -code in debug mode will trigger a panic on this case and optimized code will return `", -stringify!($SelfT), "::MIN` without a panic. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".abs(), 10); -assert_eq!((-10", stringify!($SelfT), ").abs(), 10);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[allow(unused_attributes)] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn abs(self) -> Self { - // Note that the #[inline] above means that the overflow - // semantics of the subtraction depend on the crate we're being - // inlined into. - if self.is_negative() { - -self + exp /= 2; + base = base * base; + } + + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base + } + + /// Calculates the quotient of Euclidean division of `self` by `rhs`. + /// + /// This computes the integer `n` such that `self = n * rhs + self.rem_euclid(rhs)`, + /// with `0 <= self.rem_euclid(rhs) < rhs`. + /// + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// If `self > 0`, this is equal to round towards zero (the default in Rust); + /// if `self < 0`, this is equal to round towards +/- infinity. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0 or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let a: ", stringify!($SelfT), " = 7; // or any other integer type")] + /// let b = 4; + /// + /// assert_eq!(a.div_euclid(b), 1); // 7 >= 4 * 1 + /// assert_eq!(a.div_euclid(-b), -1); // 7 >= -4 * -1 + /// assert_eq!((-a).div_euclid(b), -2); // -7 >= 4 * -2 + /// assert_eq!((-a).div_euclid(-b), 2); // -7 >= -4 * 2 + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn div_euclid(self, rhs: Self) -> Self { + let q = self / rhs; + if self % rhs < 0 { + return if rhs > 0 { q - 1 } else { q + 1 } + } + q + } + + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// This is done as if by the Euclidean division algorithm -- given + /// `r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r`, and + /// `0 <= r < abs(rhs)`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0 or the division results in overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let a: ", stringify!($SelfT), " = 7; // or any other integer type")] + /// let b = 4; + /// + /// assert_eq!(a.rem_euclid(b), 3); + /// assert_eq!((-a).rem_euclid(b), 1); + /// assert_eq!(a.rem_euclid(-b), 3); + /// assert_eq!((-a).rem_euclid(-b), 1); + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn rem_euclid(self, rhs: Self) -> Self { + let r = self % rhs; + if r < 0 { + if rhs < 0 { + r - rhs } else { - self - } - } - } - - doc_comment! { - concat!("Returns a number representing sign of `self`. - - - `0` if the number is zero - - `1` if the number is positive - - `-1` if the number is negative - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(10", stringify!($SelfT), ".signum(), 1); -assert_eq!(0", stringify!($SelfT), ".signum(), 0); -assert_eq!((-10", stringify!($SelfT), ").signum(), -1);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_sign", since = "1.47.0")] - #[inline] - pub const fn signum(self) -> Self { - match self { - n if n > 0 => 1, - 0 => 0, - _ => -1, + r + rhs } + } else { + r } } - doc_comment! { - concat!("Returns `true` if `self` is positive and `false` if the number is zero or -negative. - -# Examples - -Basic usage: - -``` -", $Feature, "assert!(10", stringify!($SelfT), ".is_positive()); -assert!(!(-10", stringify!($SelfT), ").is_positive());", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn is_positive(self) -> bool { self > 0 } - } - - doc_comment! { - concat!("Returns `true` if `self` is negative and `false` if the number is zero or -positive. - -# Examples - -Basic usage: - -``` -", $Feature, "assert!((-10", stringify!($SelfT), ").is_negative()); -assert!(!10", stringify!($SelfT), ".is_negative());", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] - pub const fn is_negative(self) -> bool { self < 0 } - } - - doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -big-endian (network) byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); -assert_eq!(bytes, ", $be_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { - self.to_be().to_ne_bytes() - } - } - -doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -little-endian byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); -assert_eq!(bytes, ", $le_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { - self.to_le().to_ne_bytes() - } - } - - doc_comment! { - concat!(" -Return the memory representation of this integer as a byte array in -native byte order. - -As the target platform's native endianness is used, portable code -should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, -instead. -", -$to_xe_bytes_doc, -" -[`to_be_bytes`]: #method.to_be_bytes -[`to_le_bytes`]: #method.to_le_bytes - -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes(); -assert_eq!( - bytes, - if cfg!(target_endian = \"big\") { - ", $be_bytes, " - } else { - ", $le_bytes, " - } -); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute them to arrays of bytes - #[rustc_allow_const_fn_unstable(const_fn_transmute)] - #[inline] - pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { - // SAFETY: integers are plain old datatypes so we can always transmute them to - // arrays of bytes - unsafe { mem::transmute(self) } - } - } - - doc_comment! { - concat!(" -Return the memory representation of this integer as a byte array in -native byte order. - -[`to_ne_bytes`] should be preferred over this whenever possible. - -[`to_ne_bytes`]: #method.to_ne_bytes -", - -" -# Examples - -``` -#![feature(num_as_ne_bytes)] -let num = ", $swap_op, stringify!($SelfT), "; -let bytes = num.as_ne_bytes(); -assert_eq!( - bytes, - if cfg!(target_endian = \"big\") { - &", $be_bytes, " - } else { - &", $le_bytes, " - } -); -```"), - #[unstable(feature = "num_as_ne_bytes", issue = "76976")] - #[inline] - pub fn as_ne_bytes(&self) -> &[u8; mem::size_of::()] { - // SAFETY: integers are plain old datatypes so we can always transmute them to - // arrays of bytes - unsafe { &*(self as *const Self as *const _) } - } - } - -doc_comment! { - concat!("Create an integer value from its representation as a byte array in -big endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_be(Self::from_ne_bytes(bytes)) - } - } - -doc_comment! { - concat!(" -Create an integer value from its representation as a byte array in -little endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_le(Self::from_ne_bytes(bytes)) - } - } - - doc_comment! { - concat!("Create an integer value from its memory representation as a byte -array in native endianness. - -As the target platform's native endianness is used, portable code -likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as -appropriate instead. - -[`from_be_bytes`]: #method.from_be_bytes -[`from_le_bytes`]: #method.from_le_bytes -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") { - ", $be_bytes, " -} else { - ", $le_bytes, " -}); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute to them - #[rustc_allow_const_fn_unstable(const_fn_transmute)] - #[inline] - pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { - // SAFETY: integers are plain old datatypes so we can always transmute to them - unsafe { mem::transmute(bytes) } - } - } - - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause a compilation warning, -new code should use [`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN) instead. - -Returns the smallest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[inline(always)] - #[rustc_promotable] - #[rustc_const_stable(feature = "const_min_value", since = "1.32.0")] - pub const fn min_value() -> Self { - Self::MIN - } - } - - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause a compilation warning, -new code should use [`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX) instead. - -Returns the largest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[inline(always)] - #[rustc_promotable] - #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] - pub const fn max_value() -> Self { - Self::MAX - } + /// Computes the absolute value of `self`. + /// + /// # Overflow behavior + /// + /// The absolute value of + #[doc = concat!("`", stringify!($SelfT), "::MIN`")] + /// cannot be represented as an + #[doc = concat!("`", stringify!($SelfT), "`,")] + /// and attempting to calculate it will cause an overflow. This means + /// that code in debug mode will trigger a panic on this case and + /// optimized code will return + #[doc = concat!("`", stringify!($SelfT), "::MIN`")] + /// without a panic. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".abs(), 10);")] + #[doc = concat!("assert_eq!((-10", stringify!($SelfT), ").abs(), 10);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[allow(unused_attributes)] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn abs(self) -> Self { + // Note that the #[inline] above means that the overflow + // semantics of the subtraction depend on the crate we're being + // inlined into. + if self.is_negative() { + -self + } else { + self + } + } + + /// Returns a number representing sign of `self`. + /// + /// - `0` if the number is zero + /// - `1` if the number is positive + /// - `-1` if the number is negative + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".signum(), 1);")] + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".signum(), 0);")] + #[doc = concat!("assert_eq!((-10", stringify!($SelfT), ").signum(), -1);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_sign", since = "1.47.0")] + #[inline] + pub const fn signum(self) -> Self { + match self { + n if n > 0 => 1, + 0 => 0, + _ => -1, + } + } + + /// Returns `true` if `self` is positive and `false` if the number is zero or + /// negative. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert!(10", stringify!($SelfT), ".is_positive());")] + #[doc = concat!("assert!(!(-10", stringify!($SelfT), ").is_positive());")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn is_positive(self) -> bool { self > 0 } + + /// Returns `true` if `self` is negative and `false` if the number is zero or + /// positive. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert!((-10", stringify!($SelfT), ").is_negative());")] + #[doc = concat!("assert!(!10", stringify!($SelfT), ".is_negative());")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] + #[inline] + pub const fn is_negative(self) -> bool { self < 0 } + + /// Return the memory representation of this integer as a byte array in + /// big-endian (network) byte order. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes();")] + #[doc = concat!("assert_eq!(bytes, ", $be_bytes, ");")] + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { + self.to_be().to_ne_bytes() + } + + /// Return the memory representation of this integer as a byte array in + /// little-endian byte order. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes();")] + #[doc = concat!("assert_eq!(bytes, ", $le_bytes, ");")] + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { + self.to_le().to_ne_bytes() + } + + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, + /// instead. + /// + #[doc = $to_xe_bytes_doc] + /// + /// [`to_be_bytes`]: #method.to_be_bytes + /// [`to_le_bytes`]: #method.to_le_bytes + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes();")] + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + #[doc = concat!(" ", $be_bytes)] + /// } else { + #[doc = concat!(" ", $le_bytes)] + /// } + /// ); + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute them to arrays of bytes + #[rustc_allow_const_fn_unstable(const_fn_transmute)] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { mem::transmute(self) } + } + + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// [`to_ne_bytes`] should be preferred over this whenever possible. + /// + /// [`to_ne_bytes`]: #method.to_ne_bytes + /// + /// # Examples + /// + /// ``` + /// #![feature(num_as_ne_bytes)] + #[doc = concat!("let num = ", $swap_op, stringify!($SelfT), ";")] + /// let bytes = num.as_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + #[doc = concat!(" &", $be_bytes)] + /// } else { + #[doc = concat!(" &", $le_bytes)] + /// } + /// ); + /// ``` + #[unstable(feature = "num_as_ne_bytes", issue = "76976")] + #[inline] + pub fn as_ne_bytes(&self) -> &[u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { &*(self as *const Self as *const _) } + } + + /// Create an integer value from its representation as a byte array in + /// big endian. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, ");")] + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_be(Self::from_ne_bytes(bytes)) + } + + /// Create an integer value from its representation as a byte array in + /// little endian. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, ");")] + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_le(Self::from_ne_bytes(bytes)) + } + + /// Create an integer value from its memory representation as a byte + /// array in native endianness. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: #method.from_be_bytes + /// [`from_le_bytes`]: #method.from_le_bytes + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") {")] + #[doc = concat!(" ", $be_bytes)] + /// } else { + #[doc = concat!(" ", $le_bytes)] + /// }); + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute to them + #[rustc_allow_const_fn_unstable(const_fn_transmute)] + #[inline] + pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + // SAFETY: integers are plain old datatypes so we can always transmute to them + unsafe { mem::transmute(bytes) } + } + + /// New code should prefer to use + #[doc = concat!("[`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN).")] + /// + /// Returns the smallest value that can be represented by this integer type. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline(always)] + #[rustc_promotable] + #[rustc_const_stable(feature = "const_min_value", since = "1.32.0")] + #[rustc_deprecated(since = "TBD", reason = "replaced by the `MIN` associated constant on this type")] + pub const fn min_value() -> Self { + Self::MIN + } + + /// New code should prefer to use + #[doc = concat!("[`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX).")] + /// + /// Returns the largest value that can be represented by this integer type. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline(always)] + #[rustc_promotable] + #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] + #[rustc_deprecated(since = "TBD", reason = "replaced by the `MAX` associated constant on this type")] + pub const fn max_value() -> Self { + Self::MAX } } } diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 9f5ae57b74..6bdfa18fa4 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -23,13 +23,6 @@ macro_rules! unlikely { }; } -macro_rules! doc_comment { - ($x:expr, $($tt:tt)*) => { - #[doc = $x] - $($tt)* - }; -} - // All these modules are technically private and only exposed for coretests: pub mod bignum; pub mod dec2flt; @@ -95,26 +88,26 @@ depending on the target pointer size. #[lang = "i8"] impl i8 { - int_impl! { i8, i8, u8, 8, -128, 127, "", "", 2, "-0x7e", "0xa", "0x12", "0x12", "0x48", + int_impl! { i8, i8, u8, 8, -128, 127, 2, "-0x7e", "0xa", "0x12", "0x12", "0x48", "[0x12]", "[0x12]", "", "" } } #[lang = "i16"] impl i16 { - int_impl! { i16, i16, u16, 16, -32768, 32767, "", "", 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", + int_impl! { i16, i16, u16, 16, -32768, 32767, 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", "", "" } } #[lang = "i32"] impl i32 { - int_impl! { i32, i32, u32, 32, -2147483648, 2147483647, "", "", 8, "0x10000b3", "0xb301", + int_impl! { i32, i32, u32, 32, -2147483648, 2147483647, 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" } } #[lang = "i64"] impl i64 { - int_impl! { i64, i64, u64, 64, -9223372036854775808, 9223372036854775807, "", "", 12, + int_impl! { i64, i64, u64, 64, -9223372036854775808, 9223372036854775807, 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", "", "" } @@ -123,7 +116,7 @@ impl i64 { #[lang = "i128"] impl i128 { int_impl! { i128, i128, u128, 128, -170141183460469231731687303715884105728, - 170141183460469231731687303715884105727, "", "", 16, + 170141183460469231731687303715884105727, 16, "0x13f40000000000000000000000004f76", "0x4f7613f4", "0x12345678901234567890123456789012", "0x12907856341290785634129078563412", "0x48091e6a2c48091e6a2c48091e6a2c48", "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, \ @@ -135,7 +128,7 @@ impl i128 { #[cfg(target_pointer_width = "16")] #[lang = "isize"] impl isize { - int_impl! { isize, i16, usize, 16, -32768, 32767, "", "", 4, "-0x5ffd", "0x3a", "0x1234", + int_impl! { isize, i16, usize, 16, -32768, 32767, 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } } @@ -143,7 +136,7 @@ impl isize { #[cfg(target_pointer_width = "32")] #[lang = "isize"] impl isize { - int_impl! { isize, i32, usize, 32, -2147483648, 2147483647, "", "", 8, "0x10000b3", "0xb301", + int_impl! { isize, i32, usize, 32, -2147483648, 2147483647, 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } @@ -152,7 +145,7 @@ impl isize { #[cfg(target_pointer_width = "64")] #[lang = "isize"] impl isize { - int_impl! { isize, i64, usize, 64, -9223372036854775808, 9223372036854775807, "", "", + int_impl! { isize, i64, usize, 64, -9223372036854775808, 9223372036854775807, 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", @@ -161,7 +154,7 @@ impl isize { #[lang = "u8"] impl u8 { - uint_impl! { u8, u8, 8, 255, "", "", 2, "0x82", "0xa", "0x12", "0x12", "0x48", "[0x12]", + uint_impl! { u8, u8, 8, 255, 2, "0x82", "0xa", "0x12", "0x12", "0x48", "[0x12]", "[0x12]", "", "" } /// Checks if the value is within the ASCII range. @@ -660,19 +653,19 @@ impl u8 { #[lang = "u16"] impl u16 { - uint_impl! { u16, u16, 16, 65535, "", "", 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", + uint_impl! { u16, u16, 16, 65535, 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", "", "" } } #[lang = "u32"] impl u32 { - uint_impl! { u32, u32, 32, 4294967295, "", "", 8, "0x10000b3", "0xb301", "0x12345678", + uint_impl! { u32, u32, 32, 4294967295, 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" } } #[lang = "u64"] impl u64 { - uint_impl! { u64, u64, 64, 18446744073709551615, "", "", 12, "0xaa00000000006e1", "0x6e10aa", + uint_impl! { u64, u64, 64, 18446744073709551615, 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", @@ -681,7 +674,7 @@ impl u64 { #[lang = "u128"] impl u128 { - uint_impl! { u128, u128, 128, 340282366920938463463374607431768211455, "", "", 16, + uint_impl! { u128, u128, 128, 340282366920938463463374607431768211455, 16, "0x13f40000000000000000000000004f76", "0x4f7613f4", "0x12345678901234567890123456789012", "0x12907856341290785634129078563412", "0x48091e6a2c48091e6a2c48091e6a2c48", "[0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, \ @@ -694,14 +687,14 @@ impl u128 { #[cfg(target_pointer_width = "16")] #[lang = "usize"] impl usize { - uint_impl! { usize, u16, 16, 65535, "", "", 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", + uint_impl! { usize, u16, 16, 65535, 4, "0xa003", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } } #[cfg(target_pointer_width = "32")] #[lang = "usize"] impl usize { - uint_impl! { usize, u32, 32, 4294967295, "", "", 8, "0x10000b3", "0xb301", "0x12345678", + uint_impl! { usize, u32, 32, 4294967295, 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } } @@ -709,7 +702,7 @@ impl usize { #[cfg(target_pointer_width = "64")] #[lang = "usize"] impl usize { - uint_impl! { usize, u64, 64, 18446744073709551615, "", "", 12, "0xaa00000000006e1", "0x6e10aa", + uint_impl! { usize, u64, 64, 18446744073709551615, 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 716b4a90e5..111feb7dbe 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1,20 +1,13 @@ //! Definitions of integer that is known not to equal zero. use crate::fmt; -use crate::ops::{BitOr, BitOrAssign}; +use crate::ops::{BitOr, BitOrAssign, Div, Rem}; use crate::str::FromStr; use super::from_str_radix; use super::{IntErrorKind, ParseIntError}; use crate::intrinsics; -macro_rules! doc_comment { - ($x:expr, $($tt:tt)*) => { - #[doc = $x] - $($tt)* - }; -} - macro_rules! impl_nonzero_fmt { ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { $( @@ -32,24 +25,21 @@ macro_rules! impl_nonzero_fmt { macro_rules! nonzero_integers { ( $( #[$stability: meta] $Ty: ident($Int: ty); )+ ) => { $( - doc_comment! { - concat!("An integer that is known not to equal zero. - -This enables some memory layout optimization. -For example, `Option<", stringify!($Ty), ">` is the same size as `", stringify!($Int), "`: - -```rust -use std::mem::size_of; -assert_eq!(size_of::>(), size_of::<", stringify!($Int), -">()); -```"), - #[$stability] - #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] - #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start(1)] - #[rustc_nonnull_optimization_guaranteed] - pub struct $Ty($Int); - } + /// An integer that is known not to equal zero. + /// + /// This enables some memory layout optimization. + #[doc = concat!("For example, `Option<", stringify!($Ty), ">` is the same size as `", stringify!($Int), "`:")] + /// + /// ```rust + /// use std::mem::size_of; + #[doc = concat!("assert_eq!(size_of::>(), size_of::<", stringify!($Int), ">());")] + /// ``` + #[$stability] + #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] + #[repr(transparent)] + #[rustc_layout_scalar_valid_range_start(1)] + #[rustc_nonnull_optimization_guaranteed] + pub struct $Ty($Int); impl $Ty { /// Creates a non-zero without checking the value. @@ -90,13 +80,10 @@ assert_eq!(size_of::>(), size_of::<", s #[stable(feature = "from_nonzero", since = "1.31.0")] impl From<$Ty> for $Int { - doc_comment! { - concat!( -"Converts a `", stringify!($Ty), "` into an `", stringify!($Int), "`"), - #[inline] - fn from(nonzero: $Ty) -> Self { - nonzero.0 - } + #[doc = concat!("Converts a `", stringify!($Ty), "` into an `", stringify!($Int), "`")] + #[inline] + fn from(nonzero: $Ty) -> Self { + nonzero.0 } } @@ -195,53 +182,49 @@ macro_rules! nonzero_leading_trailing_zeros { ( $( $Ty: ident($Uint: ty) , $LeadingTestExpr:expr ;)+ ) => { $( impl $Ty { - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -On many architectures, this function can perform better than `leading_zeros()` on the underlying integer type, as special handling of zero can be avoided. - -# Examples - -Basic usage: - -``` -#![feature(nonzero_leading_trailing_zeros)] -let n = std::num::", stringify!($Ty), "::new(", stringify!($LeadingTestExpr), ").unwrap(); - -assert_eq!(n.leading_zeros(), 0); -```"), - #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[inline] - pub const fn leading_zeros(self) -> u32 { - // SAFETY: since `self` can not be zero it is safe to call ctlz_nonzero - unsafe { intrinsics::ctlz_nonzero(self.0 as $Uint) as u32 } - } + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// On many architectures, this function can perform better than `leading_zeros()` on the underlying integer type, as special handling of zero can be avoided. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_leading_trailing_zeros)] + #[doc = concat!("let n = std::num::", stringify!($Ty), "::new(", stringify!($LeadingTestExpr), ").unwrap();")] + /// + /// assert_eq!(n.leading_zeros(), 0); + /// ``` + #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[inline] + pub const fn leading_zeros(self) -> u32 { + // SAFETY: since `self` can not be zero it is safe to call ctlz_nonzero + unsafe { intrinsics::ctlz_nonzero(self.0 as $Uint) as u32 } } - doc_comment! { - concat!("Returns the number of trailing zeros in the binary representation -of `self`. - -On many architectures, this function can perform better than `trailing_zeros()` on the underlying integer type, as special handling of zero can be avoided. - -# Examples - -Basic usage: - -``` -#![feature(nonzero_leading_trailing_zeros)] -let n = std::num::", stringify!($Ty), "::new(0b0101000).unwrap(); - -assert_eq!(n.trailing_zeros(), 3); -```"), - #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[inline] - pub const fn trailing_zeros(self) -> u32 { - // SAFETY: since `self` can not be zero it is safe to call cttz_nonzero - unsafe { intrinsics::cttz_nonzero(self.0 as $Uint) as u32 } - } + /// Returns the number of trailing zeros in the binary representation + /// of `self`. + /// + /// On many architectures, this function can perform better than `trailing_zeros()` on the underlying integer type, as special handling of zero can be avoided. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_leading_trailing_zeros)] + #[doc = concat!("let n = std::num::", stringify!($Ty), "::new(0b0101000).unwrap();")] + /// + /// assert_eq!(n.trailing_zeros(), 3); + /// ``` + #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[inline] + pub const fn trailing_zeros(self) -> u32 { + // SAFETY: since `self` can not be zero it is safe to call cttz_nonzero + unsafe { intrinsics::cttz_nonzero(self.0 as $Uint) as u32 } } } @@ -263,3 +246,83 @@ nonzero_leading_trailing_zeros! { NonZeroI128(u128), -1i128; NonZeroIsize(usize), -1isize; } + +macro_rules! nonzero_integers_div { + ( $( $Ty: ident($Int: ty); )+ ) => { + $( + #[stable(feature = "nonzero_div", since = "1.51.0")] + impl Div<$Ty> for $Int { + type Output = $Int; + /// This operation rounds towards zero, + /// truncating any fractional part of the exact result, and cannot panic. + #[inline] + fn div(self, other: $Ty) -> $Int { + // SAFETY: div by zero is checked because `other` is a nonzero, + // and MIN/-1 is checked because `self` is an unsigned int. + unsafe { crate::intrinsics::unchecked_div(self, other.get()) } + } + } + + #[stable(feature = "nonzero_div", since = "1.51.0")] + impl Rem<$Ty> for $Int { + type Output = $Int; + /// This operation satisfies `n % d == n - (n / d) * d`, and cannot panic. + #[inline] + fn rem(self, other: $Ty) -> $Int { + // SAFETY: rem by zero is checked because `other` is a nonzero, + // and MIN/-1 is checked because `self` is an unsigned int. + unsafe { crate::intrinsics::unchecked_rem(self, other.get()) } + } + } + )+ + } +} + +nonzero_integers_div! { + NonZeroU8(u8); + NonZeroU16(u16); + NonZeroU32(u32); + NonZeroU64(u64); + NonZeroU128(u128); + NonZeroUsize(usize); +} + +macro_rules! nonzero_unsigned_is_power_of_two { + ( $( $Ty: ident )+ ) => { + $( + impl $Ty { + + /// Returns `true` if and only if `self == (1 << k)` for some `k`. + /// + /// On many architectures, this function can perform better than `is_power_of_two()` + /// on the underlying integer type, as special handling of zero can be avoided. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_is_power_of_two)] + /// + #[doc = concat!("let eight = std::num::", stringify!($Ty), "::new(8).unwrap();")] + /// assert!(eight.is_power_of_two()); + #[doc = concat!("let ten = std::num::", stringify!($Ty), "::new(10).unwrap();")] + /// assert!(!ten.is_power_of_two()); + /// ``` + #[unstable(feature = "nonzero_is_power_of_two", issue = "81106")] + #[inline] + pub const fn is_power_of_two(self) -> bool { + // LLVM 11 normalizes `unchecked_sub(x, 1) & x == 0` to the implementation seen here. + // On the basic x86-64 target, this saves 3 instructions for the zero check. + // On x86_64 with BMI1, being nonzero lets it codegen to `BLSR`, which saves an instruction + // compared to the `POPCNT` implementation on the underlying integer type. + + intrinsics::ctpop(self.get()) < 2 + } + + } + )+ + } +} + +nonzero_unsigned_is_power_of_two! { NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize } diff --git a/library/core/src/num/shells/i128.rs b/library/core/src/num/shells/i128.rs index 08cb795946..785e9a4e9c 100644 --- a/library/core/src/num/shells/i128.rs +++ b/library/core/src/num/shells/i128.rs @@ -1,10 +1,13 @@ -//! The 128-bit signed integer type. +//! Constants for the 128-bit signed integer type. //! //! *[See also the `i128` primitive type](../../std/primitive.i128.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "i128", since = "1.26.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `i128`" +)] int_module! { i128, #[stable(feature = "i128", since="1.26.0")] } diff --git a/library/core/src/num/shells/i16.rs b/library/core/src/num/shells/i16.rs index 288eaceba5..48ea2e3e96 100644 --- a/library/core/src/num/shells/i16.rs +++ b/library/core/src/num/shells/i16.rs @@ -1,10 +1,13 @@ -//! The 16-bit signed integer type. +//! Constants for the 16-bit signed integer type. //! //! *[See also the `i16` primitive type](../../std/primitive.i16.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `i16`" +)] int_module! { i16 } diff --git a/library/core/src/num/shells/i32.rs b/library/core/src/num/shells/i32.rs index 0e1a2ec56c..fce6980f45 100644 --- a/library/core/src/num/shells/i32.rs +++ b/library/core/src/num/shells/i32.rs @@ -1,10 +1,13 @@ -//! The 32-bit signed integer type. +//! Constants for the 32-bit signed integer type. //! //! *[See also the `i32` primitive type](../../std/primitive.i32.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `i32`" +)] int_module! { i32 } diff --git a/library/core/src/num/shells/i64.rs b/library/core/src/num/shells/i64.rs index 27f7092710..6aa8fcf452 100644 --- a/library/core/src/num/shells/i64.rs +++ b/library/core/src/num/shells/i64.rs @@ -1,10 +1,13 @@ -//! The 64-bit signed integer type. +//! Constants for the 64-bit signed integer type. //! //! *[See also the `i64` primitive type](../../std/primitive.i64.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `i64`" +)] int_module! { i64 } diff --git a/library/core/src/num/shells/i8.rs b/library/core/src/num/shells/i8.rs index e84b421e1a..b4e0fef61b 100644 --- a/library/core/src/num/shells/i8.rs +++ b/library/core/src/num/shells/i8.rs @@ -1,10 +1,13 @@ -//! The 8-bit signed integer type. +//! Constants for the 8-bit signed integer type. //! //! *[See also the `i8` primitive type](../../std/primitive.i8.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `i8`" +)] int_module! { i8 } diff --git a/library/core/src/num/shells/int_macros.rs b/library/core/src/num/shells/int_macros.rs index ffd30b03f2..78513d44b7 100644 --- a/library/core/src/num/shells/int_macros.rs +++ b/library/core/src/num/shells/int_macros.rs @@ -1,49 +1,46 @@ #![doc(hidden)] -macro_rules! doc_comment { - ($x:expr, $($tt:tt)*) => { - #[doc = $x] - $($tt)* - }; -} - macro_rules! int_module { ($T:ident) => (int_module!($T, #[stable(feature = "rust1", since = "1.0.0")]);); ($T:ident, #[$attr:meta]) => ( - doc_comment! { - concat!("The smallest value that can be represented by this integer type. -Use [`", stringify!($T), "::MIN", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MIN) instead. - -# Examples - -```rust -// deprecated way -let min = std::", stringify!($T), "::MIN; - -// intended way -let min = ", stringify!($T), "::MIN; -``` -"), - #[$attr] - pub const MIN: $T = $T::MIN; - } - - doc_comment! { - concat!("The largest value that can be represented by this integer type. -Use [`", stringify!($T), "::MAX", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MAX) instead. - -# Examples - -```rust -// deprecated way -let max = std::", stringify!($T), "::MAX; - -// intended way -let max = ", stringify!($T), "::MAX; -``` -"), - #[$attr] - pub const MAX: $T = $T::MAX; - } + #[doc = concat!( + "The smallest value that can be represented by this integer type. Use ", + "[`", stringify!($T), "::MIN", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MIN)", + " instead.", + )] + /// + /// # Examples + /// + /// ```rust + /// // deprecated way + #[doc = concat!("let min = std::", stringify!($T), "::MIN;")] + /// + /// // intended way + #[doc = concat!("let min = ", stringify!($T), "::MIN;")] + /// ``` + /// + #[$attr] + #[rustc_deprecated(since = "TBD", reason = "replaced by the `MIN` associated constant on this type")] + pub const MIN: $T = $T::MIN; + + #[doc = concat!( + "The largest value that can be represented by this integer type. Use ", + "[`", stringify!($T), "::MAX", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MAX)", + " instead.", + )] + /// + /// # Examples + /// + /// ```rust + /// // deprecated way + #[doc = concat!("let max = std::", stringify!($T), "::MAX;")] + /// + /// // intended way + #[doc = concat!("let max = ", stringify!($T), "::MAX;")] + /// ``` + /// + #[$attr] + #[rustc_deprecated(since = "TBD", reason = "replaced by the `MAX` associated constant on this type")] + pub const MAX: $T = $T::MAX; ) } diff --git a/library/core/src/num/shells/isize.rs b/library/core/src/num/shells/isize.rs index 0dcfa4a2bd..5dc128d58a 100644 --- a/library/core/src/num/shells/isize.rs +++ b/library/core/src/num/shells/isize.rs @@ -1,10 +1,13 @@ -//! The pointer-sized signed integer type. +//! Constants for the pointer-sized signed integer type. //! //! *[See also the `isize` primitive type](../../std/primitive.isize.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `isize`" +)] int_module! { isize } diff --git a/library/core/src/num/shells/u128.rs b/library/core/src/num/shells/u128.rs index dd45ff1415..6012584ae8 100644 --- a/library/core/src/num/shells/u128.rs +++ b/library/core/src/num/shells/u128.rs @@ -1,9 +1,13 @@ -//! The 128-bit unsigned integer type. +//! Constants for the 128-bit unsigned integer type. //! //! *[See also the `u128` primitive type](../../std/primitive.u128.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "i128", since = "1.26.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `u128`" +)] + int_module! { u128, #[stable(feature = "i128", since="1.26.0")] } diff --git a/library/core/src/num/shells/u16.rs b/library/core/src/num/shells/u16.rs index 738071643b..3664119640 100644 --- a/library/core/src/num/shells/u16.rs +++ b/library/core/src/num/shells/u16.rs @@ -1,10 +1,13 @@ -//! The 16-bit unsigned integer type. +//! Constants for the 16-bit unsigned integer type. //! //! *[See also the `u16` primitive type](../../std/primitive.u16.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `u16`" +)] int_module! { u16 } diff --git a/library/core/src/num/shells/u32.rs b/library/core/src/num/shells/u32.rs index 9800c90997..f58f71423d 100644 --- a/library/core/src/num/shells/u32.rs +++ b/library/core/src/num/shells/u32.rs @@ -1,10 +1,13 @@ -//! The 32-bit unsigned integer type. +//! Constants for the 32-bit unsigned integer type. //! //! *[See also the `u32` primitive type](../../std/primitive.u32.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `u32`" +)] int_module! { u32 } diff --git a/library/core/src/num/shells/u64.rs b/library/core/src/num/shells/u64.rs index fb686c396f..2b221f66da 100644 --- a/library/core/src/num/shells/u64.rs +++ b/library/core/src/num/shells/u64.rs @@ -1,10 +1,13 @@ -//! The 64-bit unsigned integer type. +//! Constants for the 64-bit unsigned integer type. //! //! *[See also the `u64` primitive type](../../std/primitive.u64.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `u64`" +)] int_module! { u64 } diff --git a/library/core/src/num/shells/u8.rs b/library/core/src/num/shells/u8.rs index c03cbdda25..83ec60dcbd 100644 --- a/library/core/src/num/shells/u8.rs +++ b/library/core/src/num/shells/u8.rs @@ -1,10 +1,13 @@ -//! The 8-bit unsigned integer type. +//! Constants for the 8-bit unsigned integer type. //! //! *[See also the `u8` primitive type](../../std/primitive.u8.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `u8`" +)] int_module! { u8 } diff --git a/library/core/src/num/shells/usize.rs b/library/core/src/num/shells/usize.rs index a893041615..c38d521f3d 100644 --- a/library/core/src/num/shells/usize.rs +++ b/library/core/src/num/shells/usize.rs @@ -1,10 +1,13 @@ -//! The pointer-sized unsigned integer type. +//! Constants for the pointer-sized unsigned integer type. //! //! *[See also the `usize` primitive type](../../std/primitive.usize.html).* //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! New code should use the associated constants directly on the primitive type. #![stable(feature = "rust1", since = "1.0.0")] +#![rustc_deprecated( + since = "TBD", + reason = "all constants in this module replaced by associated constants on `usize`" +)] int_module! { usize } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index ae8fc18a83..9fccf3f72c 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1,928 +1,842 @@ macro_rules! uint_impl { - ($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr, $Feature:expr, $EndFeature:expr, + ($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr, $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, $reversed:expr, $le_bytes:expr, $be_bytes:expr, $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { - doc_comment! { - concat!("The smallest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MIN, 0);", $EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MIN: Self = 0; - } - - doc_comment! { - concat!("The largest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($MaxV), ");", -$EndFeature, " -```"), - #[stable(feature = "assoc_int_consts", since = "1.43.0")] - pub const MAX: Self = !0; - } + /// The smallest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN, 0);")] + /// ``` + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MIN: Self = 0; - doc_comment! { - concat!("The size of this integer type in bits. + /// The largest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($MaxV), ");")] + /// ``` + #[stable(feature = "assoc_int_consts", since = "1.43.0")] + pub const MAX: Self = !0; -# Examples + /// The size of this integer type in bits. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_bits_const)] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");")] + /// ``` + #[unstable(feature = "int_bits_const", issue = "76904")] + pub const BITS: u32 = $BITS; -``` -", $Feature, "#![feature(int_bits_const)] -assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");", -$EndFeature, " -```"), - #[unstable(feature = "int_bits_const", issue = "76904")] - pub const BITS: u32 = $BITS; + /// Converts a string slice in a given base to an integer. + /// + /// The string is expected to be an optional `+` sign + /// followed by digits. + /// Leading and trailing whitespace represent an error. + /// Digits are a subset of these characters, depending on `radix`: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_str_radix(src: &str, radix: u32) -> Result { + from_str_radix(src, radix) } - doc_comment! { - concat!("Converts a string slice in a given base to an integer. - -The string is expected to be an optional `+` sign -followed by digits. -Leading and trailing whitespace represent an error. -Digits are a subset of these characters, depending on `radix`: - -* `0-9` -* `a-z` -* `A-Z` - -# Panics - -This function panics if `radix` is not in the range from 2 to 36. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - pub fn from_str_radix(src: &str, radix: u32) -> Result { - from_str_radix(src, radix) - } + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0b01001100", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.count_ones(), 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[doc(alias = "popcount")] + #[doc(alias = "popcnt")] + #[inline] + pub const fn count_ones(self) -> u32 { + intrinsics::ctpop(self as $ActualT) as u32 } - doc_comment! { - concat!("Returns the number of ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b01001100", stringify!($SelfT), "; - -assert_eq!(n.count_ones(), 3);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[doc(alias = "popcount")] - #[doc(alias = "popcnt")] - #[inline] - pub const fn count_ones(self) -> u32 { - intrinsics::ctpop(self as $ActualT) as u32 - } + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 0);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn count_zeros(self) -> u32 { + (!self).count_ones() } - doc_comment! { - concat!("Returns the number of zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 0);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn count_zeros(self) -> u32 { - (!self).count_ones() - } + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", stringify!($SelfT), "::MAX >> 2;")] + /// + /// assert_eq!(n.leading_zeros(), 2); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn leading_zeros(self) -> u32 { + intrinsics::ctlz(self as $ActualT) as u32 } - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = ", stringify!($SelfT), "::MAX >> 2; - -assert_eq!(n.leading_zeros(), 2);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn leading_zeros(self) -> u32 { - intrinsics::ctlz(self as $ActualT) as u32 - } + /// Returns the number of trailing zeros in the binary representation + /// of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0b0101000", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.trailing_zeros(), 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn trailing_zeros(self) -> u32 { + intrinsics::cttz(self) as u32 } - doc_comment! { - concat!("Returns the number of trailing zeros in the binary representation -of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b0101000", stringify!($SelfT), "; - -assert_eq!(n.trailing_zeros(), 3);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn trailing_zeros(self) -> u32 { - intrinsics::cttz(self) as u32 - } + /// Returns the number of leading ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = !(", stringify!($SelfT), "::MAX >> 2);")] + /// + /// assert_eq!(n.leading_ones(), 2); + /// ``` + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn leading_ones(self) -> u32 { + (!self).leading_zeros() } - doc_comment! { - concat!("Returns the number of leading ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = !(", stringify!($SelfT), "::MAX >> 2); - -assert_eq!(n.leading_ones(), 2);", $EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn leading_ones(self) -> u32 { - (!self).leading_zeros() - } + /// Returns the number of trailing ones in the binary representation + /// of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0b1010111", stringify!($SelfT), ";")] + /// + /// assert_eq!(n.trailing_ones(), 3); + /// ``` + #[stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] + #[inline] + pub const fn trailing_ones(self) -> u32 { + (!self).trailing_zeros() } - doc_comment! { - concat!("Returns the number of trailing ones in the binary representation -of `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0b1010111", stringify!($SelfT), "; - -assert_eq!(n.trailing_ones(), 3);", $EndFeature, " -```"), - #[stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] - pub const fn trailing_ones(self) -> u32 { - (!self).trailing_zeros() - } + /// Shifts the bits to the left by a specified amount, `n`, + /// wrapping the truncated bits to the end of the resulting integer. + /// + /// Please note this isn't the same operation as the `<<` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $rot_op, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $rot_result, ";")] + /// + #[doc = concat!("assert_eq!(n.rotate_left(", $rot, "), m);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_left(self, n: u32) -> Self { + intrinsics::rotate_left(self, n as $SelfT) } - doc_comment! { - concat!("Shifts the bits to the left by a specified amount, `n`, -wrapping the truncated bits to the end of the resulting integer. - -Please note this isn't the same operation as the `<<` shifting operator! - -# Examples - -Basic usage: - -``` -let n = ", $rot_op, stringify!($SelfT), "; -let m = ", $rot_result, "; - -assert_eq!(n.rotate_left(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_left(self, n: u32) -> Self { - intrinsics::rotate_left(self, n as $SelfT) - } + /// Shifts the bits to the right by a specified amount, `n`, + /// wrapping the truncated bits to the beginning of the resulting + /// integer. + /// + /// Please note this isn't the same operation as the `>>` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $rot_result, stringify!($SelfT), ";")] + #[doc = concat!("let m = ", $rot_op, ";")] + /// + #[doc = concat!("assert_eq!(n.rotate_right(", $rot, "), m);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn rotate_right(self, n: u32) -> Self { + intrinsics::rotate_right(self, n as $SelfT) } - doc_comment! { - concat!("Shifts the bits to the right by a specified amount, `n`, -wrapping the truncated bits to the beginning of the resulting -integer. - -Please note this isn't the same operation as the `>>` shifting operator! - -# Examples - -Basic usage: - -``` -let n = ", $rot_result, stringify!($SelfT), "; -let m = ", $rot_op, "; - -assert_eq!(n.rotate_right(", $rot, "), m); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn rotate_right(self, n: u32) -> Self { - intrinsics::rotate_right(self, n as $SelfT) - } + /// Reverses the byte order of the integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")] + /// let m = n.swap_bytes(); + /// + #[doc = concat!("assert_eq!(m, ", $swapped, ");")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn swap_bytes(self) -> Self { + intrinsics::bswap(self as $ActualT) as Self } - doc_comment! { - concat!(" -Reverses the byte order of the integer. - -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; -let m = n.swap_bytes(); - -assert_eq!(m, ", $swapped, "); -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn swap_bytes(self) -> Self { - intrinsics::bswap(self as $ActualT) as Self - } + /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, + /// second least-significant bit becomes second most-significant bit, etc. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")] + /// let m = n.reverse_bits(); + /// + #[doc = concat!("assert_eq!(m, ", $reversed, ");")] + #[doc = concat!("assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits());")] + /// ``` + #[stable(feature = "reverse_bits", since = "1.37.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + #[must_use] + pub const fn reverse_bits(self) -> Self { + intrinsics::bitreverse(self as $ActualT) as Self } - doc_comment! { - concat!("Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, - second least-significant bit becomes second most-significant bit, etc. - -# Examples - -Basic usage: - -``` -let n = ", $swap_op, stringify!($SelfT), "; -let m = n.reverse_bits(); - -assert_eq!(m, ", $reversed, "); -assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits()); -```"), - #[stable(feature = "reverse_bits", since = "1.37.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - #[must_use] - pub const fn reverse_bits(self) -> Self { - intrinsics::bitreverse(self as $ActualT) as Self + /// Converts an integer from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "big") { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_be(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes())")] + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn from_be(x: Self) -> Self { + #[cfg(target_endian = "big")] + { + x } - } - - doc_comment! { - concat!("Converts an integer from big endian to the target's endianness. - -On big endian this is a no-op. On little endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(", stringify!($SelfT), "::from_be(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_be(n), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn from_be(x: Self) -> Self { - #[cfg(target_endian = "big")] - { - x - } - #[cfg(not(target_endian = "big"))] - { - x.swap_bytes() - } + #[cfg(not(target_endian = "big"))] + { + x.swap_bytes() } } - doc_comment! { - concat!("Converts an integer from little endian to the target's endianness. - -On little endian this is a no-op. On big endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(", stringify!($SelfT), "::from_le(n), n) -} else { - assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn from_le(x: Self) -> Self { - #[cfg(target_endian = "little")] - { - x - } - #[cfg(not(target_endian = "little"))] - { - x.swap_bytes() - } + /// Converts an integer from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "little") { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_le(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($SelfT), "::from_le(n), n.swap_bytes())")] + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn from_le(x: Self) -> Self { + #[cfg(target_endian = "little")] + { + x } - } - - doc_comment! { - concat!("Converts `self` to big endian from the target's endianness. - -On big endian this is a no-op. On little endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"big\") { - assert_eq!(n.to_be(), n) -} else { - assert_eq!(n.to_be(), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn to_be(self) -> Self { // or not to be? - #[cfg(target_endian = "big")] - { - self - } - #[cfg(not(target_endian = "big"))] - { - self.swap_bytes() - } + #[cfg(not(target_endian = "little"))] + { + x.swap_bytes() } } - doc_comment! { - concat!("Converts `self` to little endian from the target's endianness. - -On little endian this is a no-op. On big endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -", $Feature, "let n = 0x1A", stringify!($SelfT), "; - -if cfg!(target_endian = \"little\") { - assert_eq!(n.to_le(), n) -} else { - assert_eq!(n.to_le(), n.swap_bytes()) -}", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] - pub const fn to_le(self) -> Self { - #[cfg(target_endian = "little")] - { - self - } - #[cfg(not(target_endian = "little"))] - { - self.swap_bytes() - } + /// Converts `self` to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_be(), n) + /// } else { + /// assert_eq!(n.to_be(), n.swap_bytes()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn to_be(self) -> Self { // or not to be? + #[cfg(target_endian = "big")] + { + self } - } - - doc_comment! { - concat!("Checked integer addition. Computes `self + rhs`, returning `None` -if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(1), ", -"Some(", stringify!($SelfT), "::MAX - 1)); -assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_add(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_add(rhs); - if unlikely!(b) {None} else {Some(a)} + #[cfg(not(target_endian = "big"))] + { + self.swap_bytes() } } - doc_comment! { - concat!("Unchecked integer addition. Computes `self + rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), -"::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } + /// Converts `self` to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")] + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_le(), n) + /// } else { + /// assert_eq!(n.to_le(), n.swap_bytes()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_math", since = "1.32.0")] + #[inline] + pub const fn to_le(self) -> Self { + #[cfg(target_endian = "little")] + { + self } - } - - doc_comment! { - concat!("Checked integer subtraction. Computes `self - rhs`, returning -`None` if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(1", stringify!($SelfT), ".checked_sub(1), Some(0)); -assert_eq!(0", stringify!($SelfT), ".checked_sub(1), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_sub(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_sub(rhs); - if unlikely!(b) {None} else {Some(a)} + #[cfg(not(target_endian = "little"))] + { + self.swap_bytes() } } - doc_comment! { - concat!("Unchecked integer subtraction. Computes `self - rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), -"::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } - } + /// Checked integer addition. Computes `self + rhs`, returning `None` + /// if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!( + "assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(1), ", + "Some(", stringify!($SelfT), "::MAX - 1));" + )] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_add(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_add(rhs); + if unlikely!(b) {None} else {Some(a)} + } + + /// Unchecked integer addition. Computes `self + rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self + rhs > ", stringify!($SelfT), "::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_add`. + unsafe { intrinsics::unchecked_add(self, rhs) } } - doc_comment! { - concat!("Checked integer multiplication. Computes `self * rhs`, returning -`None` if overflow occurred. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".checked_mul(1), Some(5)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_mul(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_mul(rhs); - if unlikely!(b) {None} else {Some(a)} - } + /// Checked integer subtraction. Computes `self - rhs`, returning + /// `None` if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_sub(1), Some(0));")] + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".checked_sub(1), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_sub(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_sub(rhs); + if unlikely!(b) {None} else {Some(a)} + } + + /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self - rhs > ", stringify!($SelfT), "::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_sub`. + unsafe { intrinsics::unchecked_sub(self, rhs) } } - doc_comment! { - concat!("Unchecked integer multiplication. Computes `self * rhs`, assuming overflow -cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), -"::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`."), - #[unstable( - feature = "unchecked_math", - reason = "niche optimization path", - issue = "none", - )] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } - } + /// Checked integer multiplication. Computes `self * rhs`, returning + /// `None` if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_mul(1), Some(5));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_mul(self, rhs: Self) -> Option { + let (a, b) = self.overflowing_mul(rhs); + if unlikely!(b) {None} else {Some(a)} + } + + /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow + /// cannot occur. This results in undefined behavior when + #[doc = concat!("`self * rhs > ", stringify!($SelfT), "::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`.")] + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + // SAFETY: the caller must uphold the safety contract for + // `unchecked_mul`. + unsafe { intrinsics::unchecked_mul(self, rhs) } } - doc_comment! { - concat!("Checked integer division. Computes `self / rhs`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(128", stringify!($SelfT), ".checked_div(2), Some(64)); -assert_eq!(1", stringify!($SelfT), ".checked_div(0), None);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - // SAFETY: div by zero has been checked above and unsigned types have no other - // failure modes for division - Some(unsafe { intrinsics::unchecked_div(self, rhs) }) - } + /// Checked integer division. Computes `self / rhs`, returning `None` + /// if `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".checked_div(2), Some(64));")] + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_div(0), None);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + // SAFETY: div by zero has been checked above and unsigned types have no other + // failure modes for division + Some(unsafe { intrinsics::unchecked_div(self, rhs) }) } } - doc_comment! { - concat!("Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -assert_eq!(128", stringify!($SelfT), ".checked_div_euclid(2), Some(64)); -assert_eq!(1", stringify!($SelfT), ".checked_div_euclid(0), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_div_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - Some(self.div_euclid(rhs)) - } + /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None` + /// if `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".checked_div_euclid(2), Some(64));")] + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_div_euclid(0), None);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_div_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + Some(self.div_euclid(rhs)) } } - doc_comment! { - concat!("Checked integer remainder. Computes `self % rhs`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - // SAFETY: div by zero has been checked above and unsigned types have no other - // failure modes for division - Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) - } + /// Checked integer remainder. Computes `self % rhs`, returning `None` + /// if `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1));")] + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_checked_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + // SAFETY: div by zero has been checked above and unsigned types have no other + // failure modes for division + Some(unsafe { intrinsics::unchecked_rem(self, rhs) }) } } - doc_comment! { - concat!("Checked Euclidean modulo. Computes `self.rem_euclid(rhs)`, returning `None` -if `rhs == 0`. - -# Examples - -Basic usage: - -``` -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1)); -assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_rem_euclid(self, rhs: Self) -> Option { - if unlikely!(rhs == 0) { - None - } else { - Some(self.rem_euclid(rhs)) - } + /// Checked Euclidean modulo. Computes `self.rem_euclid(rhs)`, returning `None` + /// if `rhs == 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1));")] + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_rem_euclid(self, rhs: Self) -> Option { + if unlikely!(rhs == 0) { + None + } else { + Some(self.rem_euclid(rhs)) } } - doc_comment! { - concat!("Checked negation. Computes `-self`, returning `None` unless `self == -0`. - -Note that negating any positive integer will overflow. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0", stringify!($SelfT), ".checked_neg(), Some(0)); -assert_eq!(1", stringify!($SelfT), ".checked_neg(), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[inline] - pub const fn checked_neg(self) -> Option { - let (a, b) = self.overflowing_neg(); - if unlikely!(b) {None} else {Some(a)} - } + /// Checked negation. Computes `-self`, returning `None` unless `self == + /// 0`. + /// + /// Note that negating any positive integer will overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".checked_neg(), Some(0));")] + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_neg(), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[inline] + pub const fn checked_neg(self) -> Option { + let (a, b) = self.overflowing_neg(); + if unlikely!(b) {None} else {Some(a)} } - doc_comment! { - concat!("Checked shift left. Computes `self << rhs`, returning `None` -if `rhs` is larger than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10)); -assert_eq!(0x10", stringify!($SelfT), ".checked_shl(129), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shl(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) {None} else {Some(a)} - } + /// Checked shift left. Computes `self << rhs`, returning `None` + /// if `rhs` is larger than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shl(129), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shl(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shl(rhs); + if unlikely!(b) {None} else {Some(a)} } - doc_comment! { - concat!("Checked shift right. Computes `self >> rhs`, returning `None` -if `rhs` is larger than or equal to the number of bits in `self`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1)); -assert_eq!(0x10", stringify!($SelfT), ".checked_shr(129), None);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_shr(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) {None} else {Some(a)} - } + /// Checked shift right. Computes `self >> rhs`, returning `None` + /// if `rhs` is larger than or equal to the number of bits in `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(129), None);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_shr(self, rhs: u32) -> Option { + let (a, b) = self.overflowing_shr(rhs); + if unlikely!(b) {None} else {Some(a)} } - doc_comment! { - concat!("Checked exponentiation. Computes `self.pow(exp)`, returning `None` if -overflow occurred. - -# Examples - -Basic usage: + /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if + /// overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_pow(5), Some(32));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);")] + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn checked_pow(self, mut exp: u32) -> Option { + if exp == 0 { + return Some(1); + } + let mut base = self; + let mut acc: Self = 1; -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), ".checked_pow(5), Some(32)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);", $EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn checked_pow(self, mut exp: u32) -> Option { - if exp == 0 { - return Some(1); - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = try_opt!(acc.checked_mul(base)); - } - exp /= 2; - base = try_opt!(base.checked_mul(base)); + while exp > 1 { + if (exp & 1) == 1 { + acc = try_opt!(acc.checked_mul(base)); } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - - Some(try_opt!(acc.checked_mul(base))) + exp /= 2; + base = try_opt!(base.checked_mul(base)); } - } - - doc_comment! { - concat!("Saturating integer addition. Computes `self + rhs`, saturating at -the numeric bounds instead of overflowing. -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(127), ", stringify!($SelfT), "::MAX);", -$EndFeature, " -```"), + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] - pub const fn saturating_add(self, rhs: Self) -> Self { - intrinsics::saturating_add(self, rhs) - } + Some(try_opt!(acc.checked_mul(base))) } - doc_comment! { - concat!("Saturating integer subtraction. Computes `self - rhs`, saturating -at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73); -assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] - pub const fn saturating_sub(self, rhs: Self) -> Self { - intrinsics::saturating_sub(self, rhs) - } + /// Saturating integer addition. Computes `self + rhs`, saturating at + /// the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(127), ", stringify!($SelfT), "::MAX);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_add(self, rhs: Self) -> Self { + intrinsics::saturating_add(self, rhs) } - doc_comment! { - concat!("Saturating integer multiplication. Computes `self * rhs`, -saturating at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(2", stringify!($SelfT), ".saturating_mul(10), 20); -assert_eq!((", stringify!($SelfT), "::MAX).saturating_mul(10), ", stringify!($SelfT), -"::MAX);", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_mul(self, rhs: Self) -> Self { - match self.checked_mul(rhs) { - Some(x) => x, - None => Self::MAX, - } - } + /// Saturating integer subtraction. Computes `self - rhs`, saturating + /// at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73);")] + #[doc = concat!("assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[inline] + pub const fn saturating_sub(self, rhs: Self) -> Self { + intrinsics::saturating_sub(self, rhs) } - doc_comment! { - concat!("Saturating integer exponentiation. Computes `self.pow(exp)`, -saturating at the numeric bounds instead of overflowing. - -# Examples - -Basic usage: - -``` -", $Feature, " -assert_eq!(4", stringify!($SelfT), ".saturating_pow(3), 64); -assert_eq!(", stringify!($SelfT), "::MAX.saturating_pow(2), ", stringify!($SelfT), "::MAX);", -$EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Some(x) => x, - None => Self::MAX, - } + /// Saturating integer multiplication. Computes `self * rhs`, + /// saturating at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".saturating_mul(10), 20);")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX).saturating_mul(10), ", stringify!($SelfT),"::MAX);")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_mul(self, rhs: Self) -> Self { + match self.checked_mul(rhs) { + Some(x) => x, + None => Self::MAX, } } - doc_comment! { - concat!("Wrapping (modular) addition. Computes `self + rhs`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(200", stringify!($SelfT), ".wrapping_add(55), 255); -assert_eq!(200", stringify!($SelfT), ".wrapping_add(", stringify!($SelfT), "::MAX), 199);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_add(self, rhs: Self) -> Self { - intrinsics::wrapping_add(self, rhs) + /// Saturating integer exponentiation. Computes `self.pow(exp)`, + /// saturating at the numeric bounds instead of overflowing. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(4", stringify!($SelfT), ".saturating_pow(3), 64);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_pow(2), ", stringify!($SelfT), "::MAX);")] + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn saturating_pow(self, exp: u32) -> Self { + match self.checked_pow(exp) { + Some(x) => x, + None => Self::MAX, } } - doc_comment! { - concat!("Wrapping (modular) subtraction. Computes `self - rhs`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: + /// Wrapping (modular) addition. Computes `self + rhs`, + /// wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(200", stringify!($SelfT), ".wrapping_add(55), 255);")] + #[doc = concat!("assert_eq!(200", stringify!($SelfT), ".wrapping_add(", stringify!($SelfT), "::MAX), 199);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_add(self, rhs: Self) -> Self { + intrinsics::wrapping_add(self, rhs) + } -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_sub(100), 0); -assert_eq!(100", stringify!($SelfT), ".wrapping_sub(", stringify!($SelfT), "::MAX), 101);", -$EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_sub(self, rhs: Self) -> Self { - intrinsics::wrapping_sub(self, rhs) - } + /// Wrapping (modular) subtraction. Computes `self - rhs`, + /// wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_sub(100), 0);")] + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_sub(", stringify!($SelfT), "::MAX), 101);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_sub(self, rhs: Self) -> Self { + intrinsics::wrapping_sub(self, rhs) } /// Wrapping (modular) multiplication. Computes `self * @@ -936,120 +850,112 @@ $EndFeature, " /// Which explains why `u8` is used here. /// /// ``` - /// assert_eq!(10u8.wrapping_mul(12), 120); - /// assert_eq!(25u8.wrapping_mul(12), 44); + /// assert_eq!(10u8.wrapping_mul(12), 120); + /// assert_eq!(25u8.wrapping_mul(12), 44); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_mul(self, rhs: Self) -> Self { + intrinsics::wrapping_mul(self, rhs) + } + + /// Wrapping (modular) division. Computes `self / rhs`. + /// Wrapped division on unsigned types is just normal division. + /// There's no way wrapping could ever happen. + /// This function exists, so that all operations + /// are accounted for in the wrapping operations. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div(self, rhs: Self) -> Self { + self / rhs + } + + /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`. + /// Wrapped division on unsigned types is just normal division. + /// There's no way wrapping could ever happen. + /// This function exists, so that all operations + /// are accounted for in the wrapping operations. + /// Since, for the positive integers, all common + /// definitions of division are equal, this + /// is exactly equal to `self.wrapping_div(rhs)`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10);")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { + self / rhs + } + + /// Wrapping (modular) remainder. Computes `self % rhs`. + /// Wrapped remainder calculation on unsigned types is + /// just the regular remainder calculation. + /// There's no way wrapping could ever happen. + /// This function exists, so that all operations + /// are accounted for in the wrapping operations. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_rem(self, rhs: Self) -> Self { + self % rhs + } + + /// Wrapping Euclidean modulo. Computes `self.rem_euclid(rhs)`. + /// Wrapped modulo calculation on unsigned types is + /// just the regular remainder calculation. + /// There's no way wrapping could ever happen. + /// This function exists, so that all operations + /// are accounted for in the wrapping operations. + /// Since, for the positive integers, all common + /// definitions of division are equal, this + /// is exactly equal to `self.wrapping_rem(rhs)`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0);")] /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] #[must_use = "this returns the result of the operation, \ - without modifying the original"] + without modifying the original"] #[inline] - pub const fn wrapping_mul(self, rhs: Self) -> Self { - intrinsics::wrapping_mul(self, rhs) - } - - doc_comment! { - concat!("Wrapping (modular) division. Computes `self / rhs`. -Wrapped division on unsigned types is just normal division. -There's no way wrapping could ever happen. -This function exists, so that all operations -are accounted for in the wrapping operations. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div(self, rhs: Self) -> Self { - self / rhs - } - } - - doc_comment! { - concat!("Wrapping Euclidean division. Computes `self.div_euclid(rhs)`. -Wrapped division on unsigned types is just normal division. -There's no way wrapping could ever happen. -This function exists, so that all operations -are accounted for in the wrapping operations. -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self.wrapping_div(rhs)`. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { - self / rhs - } - } - - doc_comment! { - concat!("Wrapping (modular) remainder. Computes `self % rhs`. -Wrapped remainder calculation on unsigned types is -just the regular remainder calculation. -There's no way wrapping could ever happen. -This function exists, so that all operations -are accounted for in the wrapping operations. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_unstable(feature = "const_wrapping_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem(self, rhs: Self) -> Self { - self % rhs - } - } - - doc_comment! { - concat!("Wrapping Euclidean modulo. Computes `self.rem_euclid(rhs)`. -Wrapped modulo calculation on unsigned types is -just the regular remainder calculation. -There's no way wrapping could ever happen. -This function exists, so that all operations -are accounted for in the wrapping operations. -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self.wrapping_rem(rhs)`. - -# Examples - -Basic usage: - -``` -assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0); -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { - self % rhs - } + pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { + self % rhs } /// Wrapping (modular) negation. Computes `-self`, @@ -1080,167 +986,156 @@ assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0); self.overflowing_neg().0 } - doc_comment! { - concat!("Panic-free bitwise shift-left; yields `self << mask(rhs)`, -where `mask` removes any high-order bits of `rhs` that -would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-left; the -RHS of a wrapping shift-left is restricted to the range -of the type, rather than the bits shifted out of the LHS -being returned to the other end. The primitive integer -types all implement a [`rotate_left`](#method.rotate_left) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(1", stringify!($SelfT), ".wrapping_shl(7), 128); -assert_eq!(1", stringify!($SelfT), ".wrapping_shl(128), 1);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shl(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) - } + /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, + /// where `mask` removes any high-order bits of `rhs` that + /// would cause the shift to exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-left; the + /// RHS of a wrapping shift-left is restricted to the range + /// of the type, rather than the bits shifted out of the LHS + /// being returned to the other end. The primitive integer + /// types all implement a [`rotate_left`](#method.rotate_left) function, + /// which may be what you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(7), 128);")] + #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(128), 1);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shl(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shl(self, (rhs & ($BITS - 1)) as $SelfT) } } - doc_comment! { - concat!("Panic-free bitwise shift-right; yields `self >> mask(rhs)`, -where `mask` removes any high-order bits of `rhs` that -would cause the shift to exceed the bitwidth of the type. - -Note that this is *not* the same as a rotate-right; the -RHS of a wrapping shift-right is restricted to the range -of the type, rather than the bits shifted out of the LHS -being returned to the other end. The primitive integer -types all implement a [`rotate_right`](#method.rotate_right) function, -which may be what you want instead. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(128", stringify!($SelfT), ".wrapping_shr(7), 1); -assert_eq!(128", stringify!($SelfT), ".wrapping_shr(128), 128);", $EndFeature, " -```"), - #[stable(feature = "num_wrapping", since = "1.2.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_shr(self, rhs: u32) -> Self { - // SAFETY: the masking by the bitsize of the type ensures that we do not shift - // out of bounds - unsafe { - intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) - } + /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, + /// where `mask` removes any high-order bits of `rhs` that + /// would cause the shift to exceed the bitwidth of the type. + /// + /// Note that this is *not* the same as a rotate-right; the + /// RHS of a wrapping shift-right is restricted to the range + /// of the type, rather than the bits shifted out of the LHS + /// being returned to the other end. The primitive integer + /// types all implement a [`rotate_right`](#method.rotate_right) function, + /// which may be what you want instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(7), 1);")] + #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(128), 128);")] + /// ``` + #[stable(feature = "num_wrapping", since = "1.2.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_shr(self, rhs: u32) -> Self { + // SAFETY: the masking by the bitsize of the type ensures that we do not shift + // out of bounds + unsafe { + intrinsics::unchecked_shr(self, (rhs & ($BITS - 1)) as $SelfT) } } - doc_comment! { - concat!("Wrapping (modular) exponentiation. Computes `self.pow(exp)`, -wrapping around at the boundary of the type. - -# Examples - -Basic usage: + /// Wrapping (modular) exponentiation. Computes `self.pow(exp)`, + /// wrapping around at the boundary of the type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_pow(5), 243);")] + /// assert_eq!(3u8.wrapping_pow(6), 217); + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn wrapping_pow(self, mut exp: u32) -> Self { + if exp == 0 { + return 1; + } + let mut base = self; + let mut acc: Self = 1; -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".wrapping_pow(5), 243); -assert_eq!(3u8.wrapping_pow(6), 217);", $EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn wrapping_pow(self, mut exp: u32) -> Self { - if exp == 0 { - return 1; - } - let mut base = self; - let mut acc: Self = 1; - - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); - } - exp /= 2; - base = base.wrapping_mul(base); + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) + exp /= 2; + base = base.wrapping_mul(base); } - } - - doc_comment! { - concat!("Calculates `self` + `rhs` - -Returns a tuple of the addition along with a boolean indicating -whether an arithmetic overflow would occur. If an overflow would -have occurred then the wrapped value is returned. - -# Examples -Basic usage - -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false)); -assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (0, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc.wrapping_mul(base) } - doc_comment! { - concat!("Calculates `self` - `rhs` - -Returns a tuple of the subtraction along with a boolean indicating -whether an arithmetic overflow would occur. If an overflow would -have occurred then the wrapped value is returned. - -# Examples - -Basic usage + /// Calculates `self` + `rhs` + /// + /// Returns a tuple of the addition along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would + /// have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (0, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) + } -``` -", $Feature, " -assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false)); -assert_eq!(0", stringify!($SelfT), ".overflowing_sub(1), (", stringify!($SelfT), "::MAX, true));", -$EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { - let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); - (a as Self, b) - } + /// Calculates `self` - `rhs` + /// + /// Returns a tuple of the subtraction along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would + /// have occurred then the wrapped value is returned. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false));")] + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".overflowing_sub(1), (", stringify!($SelfT), "::MAX, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { + let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); + (a as Self, b) } /// Calculates the multiplication of `self` and `rhs`. @@ -1270,269 +1165,251 @@ $EndFeature, " (a as Self, b) } - doc_comment! { - concat!("Calculates the divisor when `self` is divided by `rhs`. - -Returns a tuple of the divisor along with a boolean indicating -whether an arithmetic overflow would occur. Note that for unsigned -integers overflow never occurs, so the second value is always -`false`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { - (self / rhs, false) - } - } - - doc_comment! { - concat!("Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. - -Returns a tuple of the divisor along with a boolean indicating -whether an arithmetic overflow would occur. Note that for unsigned -integers overflow never occurs, so the second value is always -`false`. -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self.overflowing_div(rhs)`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false)); -```"), - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { - (self / rhs, false) - } + /// Calculates the divisor when `self` is divided by `rhs`. + /// + /// Returns a tuple of the divisor along with a boolean indicating + /// whether an arithmetic overflow would occur. Note that for unsigned + /// integers overflow never occurs, so the second value is always + /// `false`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { + (self / rhs, false) } - doc_comment! { - concat!("Calculates the remainder when `self` is divided by `rhs`. - -Returns a tuple of the remainder after dividing along with a boolean -indicating whether an arithmetic overflow would occur. Note that for -unsigned integers overflow never occurs, so the second value is -always `false`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { - (self % rhs, false) - } + /// Calculates the quotient of Euclidean division `self.div_euclid(rhs)`. + /// + /// Returns a tuple of the divisor along with a boolean indicating + /// whether an arithmetic overflow would occur. Note that for unsigned + /// integers overflow never occurs, so the second value is always + /// `false`. + /// Since, for the positive integers, all common + /// definitions of division are equal, this + /// is exactly equal to `self.overflowing_div(rhs)`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false));")] + /// ``` + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { + (self / rhs, false) } - doc_comment! { - concat!("Calculates the remainder `self.rem_euclid(rhs)` as if by Euclidean division. - -Returns a tuple of the modulo after dividing along with a boolean -indicating whether an arithmetic overflow would occur. Note that for -unsigned integers overflow never occurs, so the second value is -always `false`. -Since, for the positive integers, all common -definitions of division are equal, this operation -is exactly equal to `self.overflowing_rem(rhs)`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage - -``` -assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false)); -```"), - #[inline] - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { - (self % rhs, false) - } + /// Calculates the remainder when `self` is divided by `rhs`. + /// + /// Returns a tuple of the remainder after dividing along with a boolean + /// indicating whether an arithmetic overflow would occur. Note that for + /// unsigned integers overflow never occurs, so the second value is + /// always `false`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_unstable(feature = "const_overflowing_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { + (self % rhs, false) } - doc_comment! { - concat!("Negates self in an overflowing fashion. - -Returns `!self + 1` using wrapping operations to return the value -that represents the negation of this unsigned value. Note that for -positive unsigned values overflow always occurs, but negating 0 does -not overflow. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false)); -assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), -", true));", $EndFeature, " -```"), - #[inline] - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - pub const fn overflowing_neg(self) -> (Self, bool) { - ((!self).wrapping_add(1), self != 0) - } + /// Calculates the remainder `self.rem_euclid(rhs)` as if by Euclidean division. + /// + /// Returns a tuple of the modulo after dividing along with a boolean + /// indicating whether an arithmetic overflow would occur. Note that for + /// unsigned integers overflow never occurs, so the second value is + /// always `false`. + /// Since, for the positive integers, all common + /// definitions of division are equal, this operation + /// is exactly equal to `self.overflowing_rem(rhs)`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false));")] + /// ``` + #[inline] + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { + (self % rhs, false) } - doc_comment! { - concat!("Shifts self left by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean -indicating whether the shift value was larger than or equal to the -number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then -used to perform the shift. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(4), (0x10, false)); -assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(132), (0x10, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) - } + /// Negates self in an overflowing fashion. + /// + /// Returns `!self + 1` using wrapping operations to return the value + /// that represents the negation of this unsigned value. Note that for + /// positive unsigned values overflow always occurs, but negating 0 does + /// not overflow. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false));")] + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), ", true));")] + /// ``` + #[inline] + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + pub const fn overflowing_neg(self) -> (Self, bool) { + ((!self).wrapping_add(1), self != 0) } - doc_comment! { - concat!("Shifts self right by `rhs` bits. - -Returns a tuple of the shifted version of self along with a boolean -indicating whether the shift value was larger than or equal to the -number of bits. If the shift value is too large, then value is -masked (N-1) where N is the number of bits, and this value is then -used to perform the shift. - -# Examples - -Basic usage - -``` -", $Feature, "assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false)); -assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(132), (0x1, true));", $EndFeature, " -```"), - #[stable(feature = "wrapping", since = "1.7.0")] - #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { - (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) - } + /// Shifts self left by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean + /// indicating whether the shift value was larger than or equal to the + /// number of bits. If the shift value is too large, then value is + /// masked (N-1) where N is the number of bits, and this value is then + /// used to perform the shift. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(4), (0x10, false));")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(132), (0x10, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) } - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -Returns a tuple of the exponentiation along with a bool indicating -whether an overflow happened. - -# Examples + /// Shifts self right by `rhs` bits. + /// + /// Returns a tuple of the shifted version of self along with a boolean + /// indicating whether the shift value was larger than or equal to the + /// number of bits. If the shift value is too large, then value is + /// masked (N-1) where N is the number of bits, and this value is then + /// used to perform the shift. + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(132), (0x1, true));")] + /// ``` + #[stable(feature = "wrapping", since = "1.7.0")] + #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { + (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) + } -Basic usage: + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// Returns a tuple of the exponentiation along with a bool indicating + /// whether an overflow happened. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".overflowing_pow(5), (243, false));")] + /// assert_eq!(3u8.overflowing_pow(6), (217, true)); + /// ``` + #[stable(feature = "no_panic_pow", since = "1.34.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { + if exp == 0{ + return (1,false); + } + let mut base = self; + let mut acc: Self = 1; + let mut overflown = false; + // Scratch space for storing results of overflowing_mul. + let mut r; -``` -", $Feature, "assert_eq!(3", stringify!($SelfT), ".overflowing_pow(5), (243, false)); -assert_eq!(3u8.overflowing_pow(6), (217, true));", $EndFeature, " -```"), - #[stable(feature = "no_panic_pow", since = "1.34.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { - if exp == 0{ - return (1,false); - } - let mut base = self; - let mut acc: Self = 1; - let mut overflown = false; - // Scratch space for storing results of overflowing_mul. - let mut r; - - while exp > 1 { - if (exp & 1) == 1 { - r = acc.overflowing_mul(base); - acc = r.0; - overflown |= r.1; - } - exp /= 2; - r = base.overflowing_mul(base); - base = r.0; + while exp > 1 { + if (exp & 1) == 1 { + r = acc.overflowing_mul(base); + acc = r.0; overflown |= r.1; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; - - r + exp /= 2; + r = base.overflowing_mul(base); + base = r.0; + overflown |= r.1; } - } - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -# Examples + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + r = acc.overflowing_mul(base); + r.1 |= overflown; -Basic usage: + r + } -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), ".pow(5), 32);", $EndFeature, " -```"), + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".pow(5), 32);")] + /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] #[must_use = "this returns the result of the operation, \ @@ -1560,84 +1437,77 @@ Basic usage: // needless overflow. acc * base } - } - - doc_comment! { - concat!("Performs Euclidean division. - -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self / rhs`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: -``` -assert_eq!(7", stringify!($SelfT), ".div_euclid(4), 1); // or any other integer type -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn div_euclid(self, rhs: Self) -> Self { - self / rhs - } + /// Performs Euclidean division. + /// + /// Since, for the positive integers, all common + /// definitions of division are equal, this + /// is exactly equal to `self / rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(7", stringify!($SelfT), ".div_euclid(4), 1); // or any other integer type")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn div_euclid(self, rhs: Self) -> Self { + self / rhs } - doc_comment! { - concat!("Calculates the least remainder of `self (mod rhs)`. - -Since, for the positive integers, all common -definitions of division are equal, this -is exactly equal to `self % rhs`. - -# Panics - -This function will panic if `rhs` is 0. - -# Examples - -Basic usage: - -``` -assert_eq!(7", stringify!($SelfT), ".rem_euclid(4), 3); // or any other integer type -```"), - #[stable(feature = "euclidean_division", since = "1.38.0")] - #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn rem_euclid(self, rhs: Self) -> Self { - self % rhs - } + /// Calculates the least remainder of `self (mod rhs)`. + /// + /// Since, for the positive integers, all common + /// definitions of division are equal, this + /// is exactly equal to `self % rhs`. + /// + /// # Panics + /// + /// This function will panic if `rhs` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(7", stringify!($SelfT), ".rem_euclid(4), 3); // or any other integer type")] + /// ``` + #[stable(feature = "euclidean_division", since = "1.38.0")] + #[rustc_const_unstable(feature = "const_euclidean_int_methods", issue = "53718")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn rem_euclid(self, rhs: Self) -> Self { + self % rhs } - doc_comment! { - concat!("Returns `true` if and only if `self == 2^k` for some `k`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert!(16", stringify!($SelfT), ".is_power_of_two()); -assert!(!10", stringify!($SelfT), ".is_power_of_two());", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_is_power_of_two", since = "1.32.0")] - #[inline] - pub const fn is_power_of_two(self) -> bool { - self.count_ones() == 1 - } + /// Returns `true` if and only if `self == 2^k` for some `k`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert!(16", stringify!($SelfT), ".is_power_of_two());")] + #[doc = concat!("assert!(!10", stringify!($SelfT), ".is_power_of_two());")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_is_power_of_two", since = "1.32.0")] + #[inline] + pub const fn is_power_of_two(self) -> bool { + self.count_ones() == 1 } // Returns one less than next power of two. @@ -1663,332 +1533,300 @@ assert!(!10", stringify!($SelfT), ".is_power_of_two());", $EndFeature, " <$SelfT>::MAX >> z } - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `self`. - -When return value overflows (i.e., `self > (1 << (N-1))` for type -`uN`), it panics in debug mode and return value is wrapped to 0 in -release mode (the only situation in which method can return 0). - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), ".next_power_of_two(), 2); -assert_eq!(3", stringify!($SelfT), ".next_power_of_two(), 4);", $EndFeature, " -```"), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - #[inline] - #[rustc_inherit_overflow_checks] - pub const fn next_power_of_two(self) -> Self { - self.one_less_than_next_power_of_two() + 1 - } - } - - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `n`. If -the next power of two is greater than the type's maximum value, -`None` is returned, otherwise the power of two is wrapped in `Some`. - -# Examples - -Basic usage: - -``` -", $Feature, "assert_eq!(2", stringify!($SelfT), -".checked_next_power_of_two(), Some(2)); -assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4)); -assert_eq!(", stringify!($SelfT), "::MAX.checked_next_power_of_two(), None);", -$EndFeature, " -```"), - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - pub const fn checked_next_power_of_two(self) -> Option { - self.one_less_than_next_power_of_two().checked_add(1) - } + /// Returns the smallest power of two greater than or equal to `self`. + /// + /// When return value overflows (i.e., `self > (1 << (N-1))` for type + /// `uN`), it panics in debug mode and return value is wrapped to 0 in + /// release mode (the only situation in which method can return 0). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".next_power_of_two(), 2);")] + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".next_power_of_two(), 4);")] + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn next_power_of_two(self) -> Self { + self.one_less_than_next_power_of_two() + 1 } - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `n`. If -the next power of two is greater than the type's maximum value, -the return value is wrapped to `0`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_next_power_of_two)] -", $Feature, " -assert_eq!(2", stringify!($SelfT), ".wrapping_next_power_of_two(), 2); -assert_eq!(3", stringify!($SelfT), ".wrapping_next_power_of_two(), 4); -assert_eq!(", stringify!($SelfT), "::MAX.wrapping_next_power_of_two(), 0);", -$EndFeature, " -```"), - #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", - reason = "needs decision on wrapping behaviour")] - #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] - pub const fn wrapping_next_power_of_two(self) -> Self { - self.one_less_than_next_power_of_two().wrapping_add(1) - } + /// Returns the smallest power of two greater than or equal to `n`. If + /// the next power of two is greater than the type's maximum value, + /// `None` is returned, otherwise the power of two is wrapped in `Some`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_next_power_of_two(), Some(2));")] + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4));")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_next_power_of_two(), None);")] + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + pub const fn checked_next_power_of_two(self) -> Option { + self.one_less_than_next_power_of_two().checked_add(1) } - doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -big-endian (network) byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes(); -assert_eq!(bytes, ", $be_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { - self.to_be().to_ne_bytes() - } + /// Returns the smallest power of two greater than or equal to `n`. If + /// the next power of two is greater than the type's maximum value, + /// the return value is wrapped to `0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_next_power_of_two)] + /// + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".wrapping_next_power_of_two(), 2);")] + #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_next_power_of_two(), 4);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_next_power_of_two(), 0);")] + /// ``` + #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", + reason = "needs decision on wrapping behaviour")] + #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] + pub const fn wrapping_next_power_of_two(self) -> Self { + self.one_less_than_next_power_of_two().wrapping_add(1) } - doc_comment! { - concat!("Return the memory representation of this integer as a byte array in -little-endian byte order. -", -$to_xe_bytes_doc, -" -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes(); -assert_eq!(bytes, ", $le_bytes, "); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { - self.to_le().to_ne_bytes() - } + /// Return the memory representation of this integer as a byte array in + /// big-endian (network) byte order. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_be_bytes();")] + #[doc = concat!("assert_eq!(bytes, ", $be_bytes, ");")] + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; mem::size_of::()] { + self.to_be().to_ne_bytes() } - doc_comment! { - concat!(" -Return the memory representation of this integer as a byte array in -native byte order. - -As the target platform's native endianness is used, portable code -should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, -instead. -", -$to_xe_bytes_doc, -" -[`to_be_bytes`]: #method.to_be_bytes -[`to_le_bytes`]: #method.to_le_bytes - -# Examples - -``` -let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes(); -assert_eq!( - bytes, - if cfg!(target_endian = \"big\") { - ", $be_bytes, " - } else { - ", $le_bytes, " - } -); -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute them to arrays of bytes - #[rustc_allow_const_fn_unstable(const_fn_transmute)] - #[inline] - pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { - // SAFETY: integers are plain old datatypes so we can always transmute them to - // arrays of bytes - unsafe { mem::transmute(self) } - } + /// Return the memory representation of this integer as a byte array in + /// little-endian byte order. + /// + #[doc = $to_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_le_bytes();")] + #[doc = concat!("assert_eq!(bytes, ", $le_bytes, ");")] + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; mem::size_of::()] { + self.to_le().to_ne_bytes() } - doc_comment! { - concat!(" -Return the memory representation of this integer as a byte array in -native byte order. - -[`to_ne_bytes`] should be preferred over this whenever possible. - -[`to_ne_bytes`]: #method.to_ne_bytes -", - -" -# Examples - -``` -#![feature(num_as_ne_bytes)] -let num = ", $swap_op, stringify!($SelfT), "; -let bytes = num.as_ne_bytes(); -assert_eq!( - bytes, - if cfg!(target_endian = \"big\") { - &", $be_bytes, " - } else { - &", $le_bytes, " - } -); -```"), - #[unstable(feature = "num_as_ne_bytes", issue = "76976")] - #[inline] - pub fn as_ne_bytes(&self) -> &[u8; mem::size_of::()] { - // SAFETY: integers are plain old datatypes so we can always transmute them to - // arrays of bytes - unsafe { &*(self as *const Self as *const _) } - } + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, + /// instead. + /// + #[doc = $to_xe_bytes_doc] + /// + /// [`to_be_bytes`]: #method.to_be_bytes + /// [`to_le_bytes`]: #method.to_le_bytes + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let bytes = ", $swap_op, stringify!($SelfT), ".to_ne_bytes();")] + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + #[doc = concat!(" ", $be_bytes)] + /// } else { + #[doc = concat!(" ", $le_bytes)] + /// } + /// ); + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute them to arrays of bytes + #[rustc_allow_const_fn_unstable(const_fn_transmute)] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { mem::transmute(self) } } - doc_comment! { - concat!("Create a native endian integer value from its representation -as a byte array in big endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_be(Self::from_ne_bytes(bytes)) - } + /// Return the memory representation of this integer as a byte array in + /// native byte order. + /// + /// [`to_ne_bytes`] should be preferred over this whenever possible. + /// + /// [`to_ne_bytes`]: #method.to_ne_bytes + /// + /// # Examples + /// + /// ``` + /// #![feature(num_as_ne_bytes)] + #[doc = concat!("let num = ", $swap_op, stringify!($SelfT), ";")] + /// let bytes = num.as_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + #[doc = concat!(" &", $be_bytes)] + /// } else { + #[doc = concat!(" &", $le_bytes)] + /// } + /// ); + /// ``` + #[unstable(feature = "num_as_ne_bytes", issue = "76976")] + #[inline] + pub fn as_ne_bytes(&self) -> &[u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { &*(self as *const Self as *const _) } } - doc_comment! { - concat!(" -Create a native endian integer value from its representation -as a byte array in little endian. -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, "); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - #[inline] - pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { - Self::from_le(Self::from_ne_bytes(bytes)) - } + /// Create a native endian integer value from its representation + /// as a byte array in big endian. + /// + #[doc = $from_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_be_bytes(", $be_bytes, ");")] + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_be_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_be_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_be(Self::from_ne_bytes(bytes)) } - doc_comment! { - concat!("Create a native endian integer value from its memory representation -as a byte array in native endianness. - -As the target platform's native endianness is used, portable code -likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as -appropriate instead. - -[`from_be_bytes`]: #method.from_be_bytes -[`from_le_bytes`]: #method.from_le_bytes -", -$from_xe_bytes_doc, -" -# Examples - -``` -let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") { - ", $be_bytes, " -} else { - ", $le_bytes, " -}); -assert_eq!(value, ", $swap_op, "); -``` - -When starting from a slice rather than an array, fallible conversion APIs can be used: - -``` -use std::convert::TryInto; - -fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " { - let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">()); - *input = rest; - ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap()) -} -```"), - #[stable(feature = "int_to_from_bytes", since = "1.32.0")] - #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] - // SAFETY: const sound because integers are plain old datatypes so we can always - // transmute to them - #[rustc_allow_const_fn_unstable(const_fn_transmute)] - #[inline] - pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { - // SAFETY: integers are plain old datatypes so we can always transmute to them - unsafe { mem::transmute(bytes) } - } + /// Create a native endian integer value from its representation + /// as a byte array in little endian. + /// + #[doc = $from_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_le_bytes(", $le_bytes, ");")] + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_le_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + #[inline] + pub const fn from_le_bytes(bytes: [u8; mem::size_of::()]) -> Self { + Self::from_le(Self::from_ne_bytes(bytes)) } - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause compilation warning, -new code should use [`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN) instead. - -Returns the smallest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_promotable] - #[inline(always)] - #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] - pub const fn min_value() -> Self { Self::MIN } + /// Create a native endian integer value from its memory representation + /// as a byte array in native endianness. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: #method.from_be_bytes + /// [`from_le_bytes`]: #method.from_le_bytes + /// + #[doc = $from_xe_bytes_doc] + /// + /// # Examples + /// + /// ``` + #[doc = concat!("let value = ", stringify!($SelfT), "::from_ne_bytes(if cfg!(target_endian = \"big\") {")] + #[doc = concat!(" ", $be_bytes, "")] + /// } else { + #[doc = concat!(" ", $le_bytes, "")] + /// }); + #[doc = concat!("assert_eq!(value, ", $swap_op, ");")] + /// ``` + /// + /// When starting from a slice rather than an array, fallible conversion APIs can be used: + /// + /// ``` + /// use std::convert::TryInto; + /// + #[doc = concat!("fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), " {")] + #[doc = concat!(" let (int_bytes, rest) = input.split_at(std::mem::size_of::<", stringify!($SelfT), ">());")] + /// *input = rest; + #[doc = concat!(" ", stringify!($SelfT), "::from_ne_bytes(int_bytes.try_into().unwrap())")] + /// } + /// ``` + #[stable(feature = "int_to_from_bytes", since = "1.32.0")] + #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] + // SAFETY: const sound because integers are plain old datatypes so we can always + // transmute to them + #[rustc_allow_const_fn_unstable(const_fn_transmute)] + #[inline] + pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { + // SAFETY: integers are plain old datatypes so we can always transmute to them + unsafe { mem::transmute(bytes) } } - doc_comment! { - concat!("**This method is soft-deprecated.** - -Although using it won’t cause compilation warning, -new code should use [`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX) instead. - -Returns the largest value that can be represented by this integer type."), - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_promotable] - #[inline(always)] - #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] - pub const fn max_value() -> Self { Self::MAX } - } + /// New code should prefer to use + #[doc = concat!("[`", stringify!($SelfT), "::MIN", "`](#associatedconstant.MIN).")] + /// instead. + /// + /// Returns the smallest value that can be represented by this integer type. + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_promotable] + #[inline(always)] + #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] + #[rustc_deprecated(since = "TBD", reason = "replaced by the `MIN` associated constant on this type")] + pub const fn min_value() -> Self { Self::MIN } + + /// New code should prefer to use + #[doc = concat!("[`", stringify!($SelfT), "::MAX", "`](#associatedconstant.MAX).")] + /// instead. + /// + /// Returns the largest value that can be represented by this integer type. + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_promotable] + #[inline(always)] + #[rustc_const_stable(feature = "const_max_value", since = "1.32.0")] + #[rustc_deprecated(since = "TBD", reason = "replaced by the `MAX` associated constant on this type")] + pub const fn max_value() -> Self { Self::MAX } } } diff --git a/library/core/src/num/wrapping.rs b/library/core/src/num/wrapping.rs index 77c9a93008..f1b9dabe7d 100644 --- a/library/core/src/num/wrapping.rs +++ b/library/core/src/num/wrapping.rs @@ -403,105 +403,94 @@ wrapping_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } macro_rules! wrapping_int_impl { ($($t:ty)*) => ($( impl Wrapping<$t> { - doc_comment! { - concat!("Returns the smallest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(>::MIN, Wrapping(", stringify!($t), "::MIN)); -```"), - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const MIN: Self = Self(<$t>::MIN); - } - - doc_comment! { - concat!("Returns the largest value that can be represented by this integer type. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(>::MAX, Wrapping(", stringify!($t), "::MAX)); -```"), - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const MAX: Self = Self(<$t>::MAX); - } - - doc_comment! { - concat!("Returns the number of ones in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; + /// Returns the smallest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(>::MIN, Wrapping(", stringify!($t), "::MIN));")] + /// ``` + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const MIN: Self = Self(<$t>::MIN); -let n = Wrapping(0b01001100", stringify!($t), "); + /// Returns the largest value that can be represented by this integer type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(>::MAX, Wrapping(", stringify!($t), "::MAX));")] + /// ``` + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const MAX: Self = Self(<$t>::MAX); -assert_eq!(n.count_ones(), 3); -```"), - #[inline] - #[doc(alias = "popcount")] - #[doc(alias = "popcnt")] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn count_ones(self) -> u32 { - self.0.count_ones() - } + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0b01001100", stringify!($t), ");")] + /// + /// assert_eq!(n.count_ones(), 3); + /// ``` + #[inline] + #[doc(alias = "popcount")] + #[doc(alias = "popcnt")] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn count_ones(self) -> u32 { + self.0.count_ones() } - doc_comment! { - concat!("Returns the number of zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(Wrapping(!0", stringify!($t), ").count_zeros(), 0); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn count_zeros(self) -> u32 { - self.0.count_zeros() - } + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(Wrapping(!0", stringify!($t), ").count_zeros(), 0);")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn count_zeros(self) -> u32 { + self.0.count_zeros() } - doc_comment! { - concat!("Returns the number of trailing zeros in the binary representation -of `self`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0b0101000", stringify!($t), "); - -assert_eq!(n.trailing_zeros(), 3); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn trailing_zeros(self) -> u32 { - self.0.trailing_zeros() - } + /// Returns the number of trailing zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0b0101000", stringify!($t), ");")] + /// + /// assert_eq!(n.trailing_zeros(), 3); + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn trailing_zeros(self) -> u32 { + self.0.trailing_zeros() } /// Shifts the bits to the left by a specified amount, `n`, @@ -608,150 +597,140 @@ assert_eq!(n.trailing_zeros(), 3); Wrapping(self.0.reverse_bits()) } - doc_comment! { - concat!("Converts an integer from big endian to the target's endianness. - -On big endian this is a no-op. On little endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0x1A", stringify!($t), "); - -if cfg!(target_endian = \"big\") { - assert_eq!(>::from_be(n), n) -} else { - assert_eq!(>::from_be(n), n.swap_bytes()) -} -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn from_be(x: Self) -> Self { - Wrapping(<$t>::from_be(x.0)) - } + /// Converts an integer from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0x1A", stringify!($t), ");")] + /// + /// if cfg!(target_endian = "big") { + #[doc = concat!(" assert_eq!(>::from_be(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(>::from_be(n), n.swap_bytes())")] + /// } + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn from_be(x: Self) -> Self { + Wrapping(<$t>::from_be(x.0)) } - doc_comment! { - concat!("Converts an integer from little endian to the target's endianness. - -On little endian this is a no-op. On big endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0x1A", stringify!($t), "); - -if cfg!(target_endian = \"little\") { - assert_eq!(>::from_le(n), n) -} else { - assert_eq!(>::from_le(n), n.swap_bytes()) -} -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn from_le(x: Self) -> Self { - Wrapping(<$t>::from_le(x.0)) - } + /// Converts an integer from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0x1A", stringify!($t), ");")] + /// + /// if cfg!(target_endian = "little") { + #[doc = concat!(" assert_eq!(>::from_le(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(>::from_le(n), n.swap_bytes())")] + /// } + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn from_le(x: Self) -> Self { + Wrapping(<$t>::from_le(x.0)) } - doc_comment! { - concat!("Converts `self` to big endian from the target's endianness. - -On big endian this is a no-op. On little endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0x1A", stringify!($t), "); - -if cfg!(target_endian = \"big\") { - assert_eq!(n.to_be(), n) -} else { - assert_eq!(n.to_be(), n.swap_bytes()) -} -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn to_be(self) -> Self { - Wrapping(self.0.to_be()) - } + /// Converts `self` to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0x1A", stringify!($t), ");")] + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_be(), n) + /// } else { + /// assert_eq!(n.to_be(), n.swap_bytes()) + /// } + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn to_be(self) -> Self { + Wrapping(self.0.to_be()) } - doc_comment! { - concat!("Converts `self` to little endian from the target's endianness. - -On little endian this is a no-op. On big endian the bytes are -swapped. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(0x1A", stringify!($t), "); - -if cfg!(target_endian = \"little\") { - assert_eq!(n.to_le(), n) -} else { - assert_eq!(n.to_le(), n.swap_bytes()) -} -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn to_le(self) -> Self { - Wrapping(self.0.to_le()) - } + /// Converts `self` to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(0x1A", stringify!($t), ");")] + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_le(), n) + /// } else { + /// assert_eq!(n.to_le(), n.swap_bytes()) + /// } + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn to_le(self) -> Self { + Wrapping(self.0.to_le()) } - doc_comment! { - concat!("Raises self to the power of `exp`, using exponentiation by squaring. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(Wrapping(3", stringify!($t), ").pow(4), Wrapping(81)); -``` - -Results that are too large are wrapped: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(Wrapping(3i8).pow(5), Wrapping(-13)); -assert_eq!(Wrapping(3i8).pow(6), Wrapping(-39)); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub fn pow(self, exp: u32) -> Self { - Wrapping(self.0.wrapping_pow(exp)) - } + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(Wrapping(3", stringify!($t), ").pow(4), Wrapping(81));")] + /// ``` + /// + /// Results that are too large are wrapped: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + /// assert_eq!(Wrapping(3i8).pow(5), Wrapping(-13)); + /// assert_eq!(Wrapping(3i8).pow(6), Wrapping(-39)); + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub fn pow(self, exp: u32) -> Self { + Wrapping(self.0.wrapping_pow(exp)) } } )*) @@ -762,124 +741,114 @@ wrapping_int_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } macro_rules! wrapping_int_impl_signed { ($($t:ty)*) => ($( impl Wrapping<$t> { - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(", stringify!($t), "::MAX) >> 2; - -assert_eq!(n.leading_zeros(), 3); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn leading_zeros(self) -> u32 { - self.0.leading_zeros() - } + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(", stringify!($t), "::MAX) >> 2;")] + /// + /// assert_eq!(n.leading_zeros(), 3); + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn leading_zeros(self) -> u32 { + self.0.leading_zeros() } - doc_comment! { - concat!("Computes the absolute value of `self`, wrapping around at -the boundary of the type. - -The only case where such wrapping can occur is when one takes the absolute value of the negative -minimal value for the type this is a positive value that is too large to represent in the type. In -such a case, this function returns `MIN` itself. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(Wrapping(100", stringify!($t), ").abs(), Wrapping(100)); -assert_eq!(Wrapping(-100", stringify!($t), ").abs(), Wrapping(100)); -assert_eq!(Wrapping(", stringify!($t), "::MIN).abs(), Wrapping(", stringify!($t), "::MIN)); -assert_eq!(Wrapping(-128i8).abs().0 as u8, 128u8); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub fn abs(self) -> Wrapping<$t> { - Wrapping(self.0.wrapping_abs()) - } + /// Computes the absolute value of `self`, wrapping around at + /// the boundary of the type. + /// + /// The only case where such wrapping can occur is when one takes the absolute value of the negative + /// minimal value for the type this is a positive value that is too large to represent in the type. In + /// such a case, this function returns `MIN` itself. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(Wrapping(100", stringify!($t), ").abs(), Wrapping(100));")] + #[doc = concat!("assert_eq!(Wrapping(-100", stringify!($t), ").abs(), Wrapping(100));")] + #[doc = concat!("assert_eq!(Wrapping(", stringify!($t), "::MIN).abs(), Wrapping(", stringify!($t), "::MIN));")] + /// assert_eq!(Wrapping(-128i8).abs().0 as u8, 128u8); + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub fn abs(self) -> Wrapping<$t> { + Wrapping(self.0.wrapping_abs()) } - doc_comment! { - concat!("Returns a number representing sign of `self`. - - - `0` if the number is zero - - `1` if the number is positive - - `-1` if the number is negative - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert_eq!(Wrapping(10", stringify!($t), ").signum(), Wrapping(1)); -assert_eq!(Wrapping(0", stringify!($t), ").signum(), Wrapping(0)); -assert_eq!(Wrapping(-10", stringify!($t), ").signum(), Wrapping(-1)); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub fn signum(self) -> Wrapping<$t> { - Wrapping(self.0.signum()) - } + /// Returns a number representing sign of `self`. + /// + /// - `0` if the number is zero + /// - `1` if the number is positive + /// - `-1` if the number is negative + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(Wrapping(10", stringify!($t), ").signum(), Wrapping(1));")] + #[doc = concat!("assert_eq!(Wrapping(0", stringify!($t), ").signum(), Wrapping(0));")] + #[doc = concat!("assert_eq!(Wrapping(-10", stringify!($t), ").signum(), Wrapping(-1));")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub fn signum(self) -> Wrapping<$t> { + Wrapping(self.0.signum()) } - doc_comment! { - concat!("Returns `true` if `self` is positive and `false` if the number is zero or -negative. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert!(Wrapping(10", stringify!($t), ").is_positive()); -assert!(!Wrapping(-10", stringify!($t), ").is_positive()); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn is_positive(self) -> bool { - self.0.is_positive() - } + /// Returns `true` if `self` is positive and `false` if the number is zero or + /// negative. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert!(Wrapping(10", stringify!($t), ").is_positive());")] + #[doc = concat!("assert!(!Wrapping(-10", stringify!($t), ").is_positive());")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn is_positive(self) -> bool { + self.0.is_positive() } - doc_comment! { - concat!("Returns `true` if `self` is negative and `false` if the number is zero or -positive. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert!(Wrapping(-10", stringify!($t), ").is_negative()); -assert!(!Wrapping(10", stringify!($t), ").is_negative()); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn is_negative(self) -> bool { - self.0.is_negative() - } + /// Returns `true` if `self` is negative and `false` if the number is zero or + /// positive. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert!(Wrapping(-10", stringify!($t), ").is_negative());")] + #[doc = concat!("assert!(!Wrapping(10", stringify!($t), ").is_negative());")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn is_negative(self) -> bool { + self.0.is_negative() } } )*) @@ -890,73 +859,67 @@ wrapping_int_impl_signed! { isize i8 i16 i32 i64 i128 } macro_rules! wrapping_int_impl_unsigned { ($($t:ty)*) => ($( impl Wrapping<$t> { - doc_comment! { - concat!("Returns the number of leading zeros in the binary representation of `self`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -let n = Wrapping(", stringify!($t), "::MAX) >> 2; - -assert_eq!(n.leading_zeros(), 2); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub const fn leading_zeros(self) -> u32 { - self.0.leading_zeros() - } + /// Returns the number of leading zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("let n = Wrapping(", stringify!($t), "::MAX) >> 2;")] + /// + /// assert_eq!(n.leading_zeros(), 2); + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub const fn leading_zeros(self) -> u32 { + self.0.leading_zeros() } - doc_comment! { - concat!("Returns `true` if and only if `self == 2^k` for some `k`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_int_impl)] -use std::num::Wrapping; - -assert!(Wrapping(16", stringify!($t), ").is_power_of_two()); -assert!(!Wrapping(10", stringify!($t), ").is_power_of_two()); -```"), - #[inline] - #[unstable(feature = "wrapping_int_impl", issue = "32463")] - pub fn is_power_of_two(self) -> bool { - self.0.is_power_of_two() - } + /// Returns `true` if and only if `self == 2^k` for some `k`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_int_impl)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert!(Wrapping(16", stringify!($t), ").is_power_of_two());")] + #[doc = concat!("assert!(!Wrapping(10", stringify!($t), ").is_power_of_two());")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_int_impl", issue = "32463")] + pub fn is_power_of_two(self) -> bool { + self.0.is_power_of_two() } - doc_comment! { - concat!("Returns the smallest power of two greater than or equal to `self`. - -When return value overflows (i.e., `self > (1 << (N-1))` for type -`uN`), overflows to `2^N = 0`. - -# Examples - -Basic usage: - -``` -#![feature(wrapping_next_power_of_two)] -use std::num::Wrapping; - -assert_eq!(Wrapping(2", stringify!($t), ").next_power_of_two(), Wrapping(2)); -assert_eq!(Wrapping(3", stringify!($t), ").next_power_of_two(), Wrapping(4)); -assert_eq!(Wrapping(200_u8).next_power_of_two(), Wrapping(0)); -```"), - #[inline] - #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", - reason = "needs decision on wrapping behaviour")] - pub fn next_power_of_two(self) -> Self { - Wrapping(self.0.wrapping_next_power_of_two()) - } + /// Returns the smallest power of two greater than or equal to `self`. + /// + /// When return value overflows (i.e., `self > (1 << (N-1))` for type + /// `uN`), overflows to `2^N = 0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(wrapping_next_power_of_two)] + /// use std::num::Wrapping; + /// + #[doc = concat!("assert_eq!(Wrapping(2", stringify!($t), ").next_power_of_two(), Wrapping(2));")] + #[doc = concat!("assert_eq!(Wrapping(3", stringify!($t), ").next_power_of_two(), Wrapping(4));")] + #[doc = concat!("assert_eq!(Wrapping(200_u8).next_power_of_two(), Wrapping(0));")] + /// ``` + #[inline] + #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", + reason = "needs decision on wrapping behaviour")] + pub fn next_power_of_two(self) -> Self { + Wrapping(self.0.wrapping_next_power_of_two()) } } )*) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 1d67e65e51..0571dc74b9 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -678,6 +678,29 @@ pub enum Bound { Unbounded, } +#[unstable(feature = "bound_as_ref", issue = "80996")] +impl Bound { + /// Converts from `&Bound` to `Bound<&T>`. + #[inline] + pub fn as_ref(&self) -> Bound<&T> { + match *self { + Included(ref x) => Included(x), + Excluded(ref x) => Excluded(x), + Unbounded => Unbounded, + } + } + + /// Converts from `&mut Bound` to `Bound<&T>`. + #[inline] + pub fn as_mut(&mut self) -> Bound<&mut T> { + match *self { + Included(ref mut x) => Included(x), + Excluded(ref mut x) => Excluded(x), + Unbounded => Unbounded, + } + } +} + impl Bound<&T> { /// Map a `Bound<&T>` to a `Bound` by cloning the contents of the bound. /// diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 1afa30f584..14e4e4da3b 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -428,6 +428,40 @@ impl Option { } } + /// Returns the contained [`Some`] value, consuming the `self` value, + /// without checking that the value is not [`None`]. + /// + /// # Safety + /// + /// Calling this method on [`None`] is *[undefined behavior]*. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// ``` + /// #![feature(option_result_unwrap_unchecked)] + /// let x = Some("air"); + /// assert_eq!(unsafe { x.unwrap_unchecked() }, "air"); + /// ``` + /// + /// ```no_run + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Option<&str> = None; + /// assert_eq!(unsafe { x.unwrap_unchecked() }, "air"); // Undefined behavior! + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")] + pub unsafe fn unwrap_unchecked(self) -> T { + debug_assert!(self.is_some()); + match self { + Some(val) => val, + // SAFETY: the safety contract must be upheld by the caller. + None => unsafe { hint::unreachable_unchecked() }, + } + } + ///////////////////////////////////////////////////////////////////////// // Transforming contained values ///////////////////////////////////////////////////////////////////////// @@ -512,7 +546,6 @@ impl Option { /// result of a function call, it is recommended to use [`ok_or_else`], which is /// lazily evaluated. /// - /// [`Result`]: Result /// [`Ok(v)`]: Ok /// [`Err(err)`]: Err /// [`Some(v)`]: Some @@ -539,7 +572,6 @@ impl Option { /// Transforms the `Option` into a [`Result`], mapping [`Some(v)`] to /// [`Ok(v)`] and [`None`] to [`Err(err())`]. /// - /// [`Result`]: Result /// [`Ok(v)`]: Ok /// [`Err(err())`]: Err /// [`Some(v)`]: Some diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 03bb849509..cbb10c324c 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -5,6 +5,40 @@ use crate::any::Any; use crate::fmt; +#[doc(hidden)] +#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] +#[allow_internal_unstable(core_panic)] +#[rustc_diagnostic_item = "core_panic_2015_macro"] +#[rustc_macro_transparency = "semitransparent"] +pub macro panic_2015 { + () => ( + $crate::panicking::panic("explicit panic") + ), + ($msg:literal $(,)?) => ( + $crate::panicking::panic($msg) + ), + ($msg:expr $(,)?) => ( + $crate::panicking::panic_str($msg) + ), + ($fmt:expr, $($arg:tt)+) => ( + $crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+)) + ), +} + +#[doc(hidden)] +#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] +#[allow_internal_unstable(core_panic)] +#[rustc_diagnostic_item = "core_panic_2021_macro"] +#[rustc_macro_transparency = "semitransparent"] +pub macro panic_2021 { + () => ( + $crate::panicking::panic("explicit panic") + ), + ($($t:tt)+) => ( + $crate::panicking::panic_fmt($crate::format_args!($($t)+)) + ), +} + /// A struct providing information about a panic. /// /// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`] diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 0b9c733f7f..b2de0e16a1 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -349,7 +349,6 @@ //! mutable reference even when you just have [`Pin`]`<&mut Self>` (such as in your own //! [`poll`] implementation). //! -//! [`Pin

`]: Pin //! [`Deref`]: crate::ops::Deref //! [`DerefMut`]: crate::ops::DerefMut //! [`mem::swap`]: crate::mem::swap @@ -364,7 +363,6 @@ //! [`RefCell`]: crate::cell::RefCell //! [`drop`]: Drop::drop //! [`VecDeque`]: ../../std/collections/struct.VecDeque.html -//! [`Option`]: Option //! [`Some(v)`]: Some //! [`ptr::write`]: crate::ptr::write //! [`Future`]: crate::future::Future diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 8fd9ff768c..28de28c70e 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -232,23 +232,27 @@ impl *const T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. /// - /// In other words, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::())` - /// is *not* the same as `y`, and dereferencing it is undefined behavior - /// unless `x` and `y` point into the same allocated object. + /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. /// - /// Compared to [`offset`], this method basically delays the requirement of staying - /// within the same allocated object: [`offset`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_offset` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`offset`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// Compared to [`offset`], this method basically delays the requirement of staying within the + /// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_offset` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`offset`] + /// can be optimized better and is thus preferable in performance-sensitive code. + /// + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_offset(o).wrapping_offset(o.wrapping_neg())` is always the same as `x`. In other + /// words, leaving the allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -571,19 +575,27 @@ impl *const T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. + /// + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. + /// + /// In other words, `let z = x.wrapping_add((y as usize) - (x as usize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// Compared to [`add`], this method basically delays the requirement of staying within the + /// same allocated object: [`add`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_add` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`add`] + /// can be optimized better and is thus preferable in performance-sensitive code. /// - /// Compared to [`add`], this method basically delays the requirement of staying - /// within the same allocated object: [`add`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_add` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`add`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the + /// allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -621,26 +633,34 @@ impl *const T { } /// Calculates the offset from a pointer using wrapping arithmetic. - /// (convenience for `.wrapping_offset((count as isize).wrapping_sub())`) + /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`) /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. + /// + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. + /// + /// In other words, `let z = x.wrapping_sub((x as usize) - (y as usize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// Compared to [`sub`], this method basically delays the requirement of staying within the + /// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_sub` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`sub`] + /// can be optimized better and is thus preferable in performance-sensitive code. /// - /// Compared to [`sub`], this method basically delays the requirement of staying - /// within the same allocated object: [`sub`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_sub` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`sub`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the + /// allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -725,8 +745,9 @@ impl *const T { /// /// [`ptr::read`]: crate::ptr::read() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] #[inline] - pub unsafe fn read(self) -> T + pub const unsafe fn read(self) -> T where T: Sized, { @@ -763,8 +784,9 @@ impl *const T { /// /// [`ptr::read_unaligned`]: crate::ptr::read_unaligned() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] #[inline] - pub unsafe fn read_unaligned(self) -> T + pub const unsafe fn read_unaligned(self) -> T where T: Sized, { diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 27d49529a5..c0108c0f82 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -685,8 +685,8 @@ pub unsafe fn replace(dst: *mut T, mut src: T) -> T { /// [valid]: self#safety #[inline] #[stable(feature = "rust1", since = "1.0.0")] -pub unsafe fn read(src: *const T) -> T { - // `copy_nonoverlapping` takes care of debug_assert. +#[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] +pub const unsafe fn read(src: *const T) -> T { let mut tmp = MaybeUninit::::uninit(); // SAFETY: the caller must guarantee that `src` is valid for reads. // `src` cannot overlap `tmp` because `tmp` was just allocated on @@ -784,8 +784,8 @@ pub unsafe fn read(src: *const T) -> T { /// ``` #[inline] #[stable(feature = "ptr_unaligned", since = "1.17.0")] -pub unsafe fn read_unaligned(src: *const T) -> T { - // `copy_nonoverlapping` takes care of debug_assert. +#[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] +pub const unsafe fn read_unaligned(src: *const T) -> T { let mut tmp = MaybeUninit::::uninit(); // SAFETY: the caller must guarantee that `src` is valid for reads. // `src` cannot overlap `tmp` because `tmp` was just allocated on @@ -881,12 +881,19 @@ pub unsafe fn read_unaligned(src: *const T) -> T { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn write(dst: *mut T, src: T) { - if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) { - // Not panicking to keep codegen impact smaller. - abort(); + // We are calling the intrinsics directly to avoid function calls in the generated code + // as `intrinsics::copy_nonoverlapping` is a wrapper function. + extern "rust-intrinsic" { + fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); + } + + // SAFETY: the caller must guarantee that `dst` is valid for writes. + // `dst` cannot overlap `src` because the caller has mutable access + // to `dst` while `src` is owned by this function. + unsafe { + copy_nonoverlapping(&src as *const T, dst, 1); + intrinsics::forget(src); } - // SAFETY: the caller must uphold the safety contract for `move_val_init`. - unsafe { intrinsics::move_val_init(&mut *dst, src) } } /// Overwrites a memory location with the given value without reading or @@ -979,7 +986,6 @@ pub unsafe fn write_unaligned(dst: *mut T, src: T) { // `dst` cannot overlap `src` because the caller has mutable access // to `dst` while `src` is owned by this function. unsafe { - // `copy_nonoverlapping` takes care of debug_assert. copy_nonoverlapping(&src as *const T as *const u8, dst as *mut u8, mem::size_of::()); } mem::forget(src); @@ -1495,7 +1501,6 @@ fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L } /// # Example /// /// ``` -/// #![feature(raw_ref_macros)] /// use std::ptr; /// /// #[repr(packed)] @@ -1506,14 +1511,14 @@ fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L } /// /// let packed = Packed { f1: 1, f2: 2 }; /// // `&packed.f2` would create an unaligned reference, and thus be Undefined Behavior! -/// let raw_f2 = ptr::raw_const!(packed.f2); +/// let raw_f2 = ptr::addr_of!(packed.f2); /// assert_eq!(unsafe { raw_f2.read_unaligned() }, 2); /// ``` -#[unstable(feature = "raw_ref_macros", issue = "73394")] +#[stable(feature = "raw_ref_macros", since = "1.51.0")] #[rustc_macro_transparency = "semitransparent"] #[allow_internal_unstable(raw_ref_op)] -pub macro raw_const($e:expr) { - &raw const $e +pub macro addr_of($place:expr) { + &raw const $place } /// Create a `mut` raw pointer to a place, without creating an intermediate reference. @@ -1528,7 +1533,6 @@ pub macro raw_const($e:expr) { /// # Example /// /// ``` -/// #![feature(raw_ref_macros)] /// use std::ptr; /// /// #[repr(packed)] @@ -1539,13 +1543,13 @@ pub macro raw_const($e:expr) { /// /// let mut packed = Packed { f1: 1, f2: 2 }; /// // `&mut packed.f2` would create an unaligned reference, and thus be Undefined Behavior! -/// let raw_f2 = ptr::raw_mut!(packed.f2); +/// let raw_f2 = ptr::addr_of_mut!(packed.f2); /// unsafe { raw_f2.write_unaligned(42); } /// assert_eq!({packed.f2}, 42); // `{...}` forces copying the field instead of creating a reference. /// ``` -#[unstable(feature = "raw_ref_macros", issue = "73394")] +#[stable(feature = "raw_ref_macros", since = "1.51.0")] #[rustc_macro_transparency = "semitransparent"] #[allow_internal_unstable(raw_ref_op)] -pub macro raw_mut($e:expr) { - &raw mut $e +pub macro addr_of_mut($place:expr) { + &raw mut $place } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5f94c2393a..99744fc711 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -238,23 +238,27 @@ impl *mut T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. /// - /// In other words, `x.wrapping_offset((y as usize).wrapping_sub(x as usize) / size_of::())` - /// is *not* the same as `y`, and dereferencing it is undefined behavior - /// unless `x` and `y` point into the same allocated object. + /// In other words, `let z = x.wrapping_offset((y as isize) - (x as isize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. /// - /// Compared to [`offset`], this method basically delays the requirement of staying - /// within the same allocated object: [`offset`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_offset` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`offset`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// Compared to [`offset`], this method basically delays the requirement of staying within the + /// same allocated object: [`offset`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_offset` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`offset`] + /// can be optimized better and is thus preferable in performance-sensitive code. + /// + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_offset(o).wrapping_offset(o.wrapping_neg())` is always the same as `x`. In other + /// words, leaving the allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -678,19 +682,27 @@ impl *mut T { /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. + /// + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. + /// + /// In other words, `let z = x.wrapping_add((y as usize) - (x as usize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// Compared to [`add`], this method basically delays the requirement of staying within the + /// same allocated object: [`add`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_add` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`add`] + /// can be optimized better and is thus preferable in performance-sensitive code. /// - /// Compared to [`add`], this method basically delays the requirement of staying - /// within the same allocated object: [`add`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_add` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`add`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the + /// allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -728,26 +740,34 @@ impl *mut T { } /// Calculates the offset from a pointer using wrapping arithmetic. - /// (convenience for `.wrapping_offset((count as isize).wrapping_sub())`) + /// (convenience for `.wrapping_offset((count as isize).wrapping_neg())`) /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// The resulting pointer does not need to be in bounds, but it is - /// potentially hazardous to dereference (which requires `unsafe`). + /// This operation itself is always safe, but using the resulting pointer is not. + /// + /// The resulting pointer remains attached to the same allocated object that `self` points to. + /// It may *not* be used to access a different allocated object. Note that in Rust, every + /// (stack-allocated) variable is considered a separate allocated object. + /// + /// In other words, `let z = x.wrapping_sub((x as usize) - (y as usize))` does *not* make `z` + /// the same as `y` even if we assume `T` has size `1` and there is no overflow: `z` is still + /// attached to the object `x` is attached to, and dereferencing it is Undefined Behavior unless + /// `x` and `y` point into the same allocated object. /// - /// In particular, the resulting pointer remains attached to the same allocated - /// object that `self` points to. It may *not* be used to access a - /// different allocated object. Note that in Rust, - /// every (stack-allocated) variable is considered a separate allocated object. + /// Compared to [`sub`], this method basically delays the requirement of staying within the + /// same allocated object: [`sub`] is immediate Undefined Behavior when crossing object + /// boundaries; `wrapping_sub` produces a pointer but still leads to Undefined Behavior if a + /// pointer is dereferenced when it is out-of-bounds of the object it is attached to. [`sub`] + /// can be optimized better and is thus preferable in performance-sensitive code. /// - /// Compared to [`sub`], this method basically delays the requirement of staying - /// within the same allocated object: [`sub`] is immediate Undefined Behavior when - /// crossing object boundaries; `wrapping_sub` produces a pointer but still leads - /// to Undefined Behavior if that pointer is dereferenced. [`sub`] can be optimized - /// better and is thus preferable in performance-sensitive code. + /// The delayed check only considers the value of the pointer that was dereferenced, not the + /// intermediate values used during the computation of the final result. For example, + /// `x.wrapping_add(o).wrapping_sub(o)` is always the same as `x`. In other words, leaving the + /// allocated object and then re-entering it later is permitted. /// /// If you need to cross object boundaries, cast the pointer to an integer and /// do the arithmetic there. @@ -832,8 +852,9 @@ impl *mut T { /// /// [`ptr::read`]: crate::ptr::read() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] #[inline] - pub unsafe fn read(self) -> T + pub const unsafe fn read(self) -> T where T: Sized, { @@ -870,8 +891,9 @@ impl *mut T { /// /// [`ptr::read_unaligned`]: crate::ptr::read_unaligned() #[stable(feature = "pointer_methods", since = "1.26.0")] + #[rustc_const_unstable(feature = "const_ptr_read", issue = "80377")] #[inline] - pub unsafe fn read_unaligned(self) -> T + pub const unsafe fn read_unaligned(self) -> T where T: Sized, { diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index d849008b88..e45fefc7ed 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -19,12 +19,19 @@ use crate::slice::{self, SliceIndex}; /// as a discriminant -- `Option>` has the same size as `*mut T`. /// However the pointer may still dangle if it isn't dereferenced. /// -/// Unlike `*mut T`, `NonNull` is covariant over `T`. If this is incorrect -/// for your use case, you should include some [`PhantomData`] in your type to -/// provide invariance, such as `PhantomData>` or `PhantomData<&'a mut T>`. -/// Usually this won't be necessary; covariance is correct for most safe abstractions, -/// such as `Box`, `Rc`, `Arc`, `Vec`, and `LinkedList`. This is the case because they -/// provide a public API that follows the normal shared XOR mutable rules of Rust. +/// Unlike `*mut T`, `NonNull` was chosen to be covariant over `T`. This makes it +/// possible to use `NonNull` when building covariant types, but introduces the +/// risk of unsoundness if used in a type that shouldn't actually be covariant. +/// (The opposite choice was made for `*mut T` even though technically the unsoundness +/// could only be caused by calling unsafe functions.) +/// +/// Covariance is correct for most safe abstractions, such as `Box`, `Rc`, `Arc`, `Vec`, +/// and `LinkedList`. This is the case because they provide a public API that follows the +/// normal shared XOR mutable rules of Rust. +/// +/// If your type cannot safely be covariant, you must ensure it contains some +/// additional field to provide invariance. Often this field will be a [`PhantomData`] +/// type like `PhantomData>` or `PhantomData<&'a mut T>`. /// /// Notice that `NonNull` has a `From` instance for `&T`. However, this does /// not change the fact that mutating through a (pointer derived from a) shared diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 0b4ca2b721..a43ba5882e 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -229,7 +229,7 @@ use crate::iter::{self, FromIterator, FusedIterator, TrustedLen}; use crate::ops::{self, Deref, DerefMut}; -use crate::{convert, fmt}; +use crate::{convert, fmt, hint}; /// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]). /// @@ -368,8 +368,6 @@ impl Result { /// Converts `self` into an [`Option`], consuming `self`, /// and discarding the error, if any. /// - /// [`Option`]: Option - /// /// # Examples /// /// Basic usage: @@ -395,8 +393,6 @@ impl Result { /// Converts `self` into an [`Option`], consuming `self`, /// and discarding the success value, if any. /// - /// [`Option`]: Option - /// /// # Examples /// /// Basic usage: @@ -825,6 +821,74 @@ impl Result { Err(e) => op(e), } } + + /// Returns the contained [`Ok`] value, consuming the `self` value, + /// without checking that the value is not an [`Err`]. + /// + /// # Safety + /// + /// Calling this method on an [`Err`] is *[undefined behavior]*. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// ``` + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Ok(2); + /// assert_eq!(unsafe { x.unwrap_unchecked() }, 2); + /// ``` + /// + /// ```no_run + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Err("emergency failure"); + /// unsafe { x.unwrap_unchecked(); } // Undefined behavior! + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")] + pub unsafe fn unwrap_unchecked(self) -> T { + debug_assert!(self.is_ok()); + match self { + Ok(t) => t, + // SAFETY: the safety contract must be upheld by the caller. + Err(_) => unsafe { hint::unreachable_unchecked() }, + } + } + + /// Returns the contained [`Err`] value, consuming the `self` value, + /// without checking that the value is not an [`Ok`]. + /// + /// # Safety + /// + /// Calling this method on an [`Ok`] is *[undefined behavior]*. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + /// + /// # Examples + /// + /// ```no_run + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Ok(2); + /// unsafe { x.unwrap_err_unchecked() }; // Undefined behavior! + /// ``` + /// + /// ``` + /// #![feature(option_result_unwrap_unchecked)] + /// let x: Result = Err("emergency failure"); + /// assert_eq!(unsafe { x.unwrap_err_unchecked() }, "emergency failure"); + /// ``` + #[inline] + #[track_caller] + #[unstable(feature = "option_result_unwrap_unchecked", reason = "newly added", issue = "81383")] + pub unsafe fn unwrap_err_unchecked(self) -> E { + debug_assert!(self.is_err()); + match self { + // SAFETY: the safety contract must be upheld by the caller. + Ok(_) => unsafe { hint::unreachable_unchecked() }, + Err(e) => e, + } + } } impl Result<&T, E> { @@ -1009,8 +1073,6 @@ impl Result { /// Panics if the value is an [`Ok`], with a custom panic message provided /// by the [`Ok`]'s value. /// - /// - /// /// # Examples /// /// ```{.should_panic} diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 18073f4afe..72af47c71d 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -75,28 +75,6 @@ where } } -// Use an equal-pointer optimization when types are `Eq` -// We can't make `A` and `B` the same type because `min_specialization` won't -// allow it. -impl SlicePartialEq for [A] -where - A: MarkerEq, -{ - default fn equal(&self, other: &[B]) -> bool { - if self.len() != other.len() { - return false; - } - - // While performance would suffer if `guaranteed_eq` just returned `false` - // for all arguments, correctness and return value of this function are not affected. - if self.as_ptr().guaranteed_eq(other.as_ptr() as *const A) { - return true; - } - - self.iter().zip(other.iter()).all(|(x, y)| x == y) - } -} - // Use memcmp for bytewise equality when the types allow impl SlicePartialEq for [A] where @@ -107,11 +85,6 @@ where return false; } - // While performance would suffer if `guaranteed_eq` just returned `false` - // for all arguments, correctness and return value of this function are not affected. - if self.as_ptr().guaranteed_eq(other.as_ptr() as *const A) { - return true; - } // SAFETY: `self` and `other` are references and are thus guaranteed to be valid. // The two slices have been checked to have the same size above. unsafe { diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index e373936a6c..50664267a6 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! Definitions of a bunch of iterators for `[T]`. #[macro_use] // import iterator! and forward_iterator! @@ -50,7 +51,7 @@ fn size_from_ptr(_: *const T) -> usize { /// Basic usage: /// /// ``` -/// // First, we declare a type which has `iter` method to get the `Iter` struct (&[usize here]): +/// // First, we declare a type which has `iter` method to get the `Iter` struct (`&[usize]` here): /// let slice = &[1, 2, 3]; /// /// // Then, we iterate over it: @@ -111,7 +112,7 @@ impl<'a, T> Iter<'a, T> { /// /// ``` /// // First, we declare a type which has the `iter` method to get the `Iter` - /// // struct (&[usize here]): + /// // struct (`&[usize]` here): /// let slice = &[1, 2, 3]; /// /// // Then, we get the iterator: @@ -166,7 +167,7 @@ impl AsRef<[T]> for Iter<'_, T> { /// /// ``` /// // First, we declare a type which has `iter_mut` method to get the `IterMut` -/// // struct (&[usize here]): +/// // struct (`&[usize]` here): /// let mut slice = &mut [1, 2, 3]; /// /// // Then, we iterate over it and increment each element value: @@ -245,7 +246,7 @@ impl<'a, T> IterMut<'a, T> { /// /// ``` /// // First, we declare a type which has `iter_mut` method to get the `IterMut` - /// // struct (&[usize here]): + /// // struct (`&[usize]` here): /// let mut slice = &mut [1, 2, 3]; /// /// { @@ -445,15 +446,13 @@ impl FusedIterator for Split<'_, T, P> where P: FnMut(&T) -> bool {} /// # Example /// /// ``` -/// #![feature(split_inclusive)] -/// /// let slice = [10, 40, 33, 20]; /// let mut iter = slice.split_inclusive(|num| num % 3 == 0); /// ``` /// /// [`split_inclusive`]: ../../std/primitive.slice.html#method.split_inclusive /// [slices]: ../../std/primitive.slice.html -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] pub struct SplitInclusive<'a, T: 'a, P> where P: FnMut(&T) -> bool, @@ -470,7 +469,7 @@ impl<'a, T: 'a, P: FnMut(&T) -> bool> SplitInclusive<'a, T, P> { } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl fmt::Debug for SplitInclusive<'_, T, P> where P: FnMut(&T) -> bool, @@ -484,7 +483,7 @@ where } // FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl Clone for SplitInclusive<'_, T, P> where P: Clone + FnMut(&T) -> bool, @@ -494,7 +493,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl<'a, T, P> Iterator for SplitInclusive<'a, T, P> where P: FnMut(&T) -> bool, @@ -523,7 +522,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl<'a, T, P> DoubleEndedIterator for SplitInclusive<'a, T, P> where P: FnMut(&T) -> bool, @@ -548,7 +547,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl FusedIterator for SplitInclusive<'_, T, P> where P: FnMut(&T) -> bool {} /// An iterator over the mutable subslices of the vector which are separated @@ -688,15 +687,13 @@ impl FusedIterator for SplitMut<'_, T, P> where P: FnMut(&T) -> bool {} /// # Example /// /// ``` -/// #![feature(split_inclusive)] -/// /// let mut v = [10, 40, 30, 20, 60, 50]; /// let iter = v.split_inclusive_mut(|num| *num % 3 == 0); /// ``` /// /// [`split_inclusive_mut`]: ../../std/primitive.slice.html#method.split_inclusive_mut /// [slices]: ../../std/primitive.slice.html -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] pub struct SplitInclusiveMut<'a, T: 'a, P> where P: FnMut(&T) -> bool, @@ -713,7 +710,7 @@ impl<'a, T: 'a, P: FnMut(&T) -> bool> SplitInclusiveMut<'a, T, P> { } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl fmt::Debug for SplitInclusiveMut<'_, T, P> where P: FnMut(&T) -> bool, @@ -726,7 +723,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl<'a, T, P> Iterator for SplitInclusiveMut<'a, T, P> where P: FnMut(&T) -> bool, @@ -766,7 +763,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl<'a, T, P> DoubleEndedIterator for SplitInclusiveMut<'a, T, P> where P: FnMut(&T) -> bool, @@ -800,7 +797,7 @@ where } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl FusedIterator for SplitInclusiveMut<'_, T, P> where P: FnMut(&T) -> bool {} /// An iterator over subslices separated by elements that match a predicate @@ -2967,3 +2964,176 @@ unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> { false } } + +/// An iterator over slice in (non-overlapping) chunks separated by a predicate. +/// +/// This struct is created by the [`group_by`] method on [slices]. +/// +/// [`group_by`]: ../../std/primitive.slice.html#method.group_by +/// [slices]: ../../std/primitive.slice.html +#[unstable(feature = "slice_group_by", issue = "80552")] +pub struct GroupBy<'a, T: 'a, P> { + slice: &'a [T], + predicate: P, +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> GroupBy<'a, T, P> { + pub(super) fn new(slice: &'a [T], predicate: P) -> Self { + GroupBy { slice, predicate } + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> Iterator for GroupBy<'a, T, P> +where + P: FnMut(&T, &T) -> bool, +{ + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option { + if self.slice.is_empty() { + None + } else { + let mut len = 1; + let mut iter = self.slice.windows(2); + while let Some([l, r]) = iter.next() { + if (self.predicate)(l, r) { len += 1 } else { break } + } + let (head, tail) = self.slice.split_at(len); + self.slice = tail; + Some(head) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.slice.is_empty() { (0, Some(0)) } else { (1, Some(self.slice.len())) } + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> DoubleEndedIterator for GroupBy<'a, T, P> +where + P: FnMut(&T, &T) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option { + if self.slice.is_empty() { + None + } else { + let mut len = 1; + let mut iter = self.slice.windows(2); + while let Some([l, r]) = iter.next_back() { + if (self.predicate)(l, r) { len += 1 } else { break } + } + let (head, tail) = self.slice.split_at(self.slice.len() - len); + self.slice = head; + Some(tail) + } + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> FusedIterator for GroupBy<'a, T, P> where P: FnMut(&T, &T) -> bool {} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for GroupBy<'a, T, P> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GroupBy").field("slice", &self.slice).finish() + } +} + +/// An iterator over slice in (non-overlapping) mutable chunks separated +/// by a predicate. +/// +/// This struct is created by the [`group_by_mut`] method on [slices]. +/// +/// [`group_by_mut`]: ../../std/primitive.slice.html#method.group_by_mut +/// [slices]: ../../std/primitive.slice.html +#[unstable(feature = "slice_group_by", issue = "80552")] +pub struct GroupByMut<'a, T: 'a, P> { + slice: &'a mut [T], + predicate: P, +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> GroupByMut<'a, T, P> { + pub(super) fn new(slice: &'a mut [T], predicate: P) -> Self { + GroupByMut { slice, predicate } + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> Iterator for GroupByMut<'a, T, P> +where + P: FnMut(&T, &T) -> bool, +{ + type Item = &'a mut [T]; + + #[inline] + fn next(&mut self) -> Option { + if self.slice.is_empty() { + None + } else { + let mut len = 1; + let mut iter = self.slice.windows(2); + while let Some([l, r]) = iter.next() { + if (self.predicate)(l, r) { len += 1 } else { break } + } + let slice = mem::take(&mut self.slice); + let (head, tail) = slice.split_at_mut(len); + self.slice = tail; + Some(head) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.slice.is_empty() { (0, Some(0)) } else { (1, Some(self.slice.len())) } + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> DoubleEndedIterator for GroupByMut<'a, T, P> +where + P: FnMut(&T, &T) -> bool, +{ + #[inline] + fn next_back(&mut self) -> Option { + if self.slice.is_empty() { + None + } else { + let mut len = 1; + let mut iter = self.slice.windows(2); + while let Some([l, r]) = iter.next_back() { + if (self.predicate)(l, r) { len += 1 } else { break } + } + let slice = mem::take(&mut self.slice); + let (head, tail) = slice.split_at_mut(slice.len() - len); + self.slice = head; + Some(tail) + } + } +} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a, P> FusedIterator for GroupByMut<'a, T, P> where P: FnMut(&T, &T) -> bool {} + +#[unstable(feature = "slice_group_by", issue = "80552")] +impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for GroupByMut<'a, T, P> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GroupByMut").field("slice", &self.slice).finish() + } +} diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index f5af48e0dd..19a3b45e56 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -57,7 +57,10 @@ pub use iter::{ArrayChunks, ArrayChunksMut}; #[unstable(feature = "array_windows", issue = "75027")] pub use iter::ArrayWindows; -#[unstable(feature = "split_inclusive", issue = "72360")] +#[unstable(feature = "slice_group_by", issue = "80552")] +pub use iter::{GroupBy, GroupByMut}; + +#[stable(feature = "split_inclusive", since = "1.51.0")] pub use iter::{SplitInclusive, SplitInclusiveMut}; #[stable(feature = "rust1", since = "1.0.0")] @@ -84,6 +87,7 @@ impl [T] { /// let a = [1, 2, 3]; /// assert_eq!(a.len(), 3); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_len", since = "1.32.0")] #[inline] @@ -538,10 +542,9 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn swap(&mut self, a: usize, b: usize) { - // Can't take two mutable loans from one vector, so instead just cast - // them to their raw pointers to do the swap. - let pa: *mut T = &mut self[a]; - let pb: *mut T = &mut self[b]; + // Can't take two mutable loans from one vector, so instead use raw pointers. + let pa = ptr::addr_of_mut!(self[a]); + let pb = ptr::addr_of_mut!(self[b]); // SAFETY: `pa` and `pb` have been created from safe mutable references and refer // to elements in the slice and therefore are guaranteed to be valid and aligned. // Note that accessing the elements behind `a` and `b` is checked and will @@ -884,6 +887,46 @@ impl [T] { ChunksExactMut::new(self, chunk_size) } + /// Splits the slice into a slice of `N`-element arrays, + /// assuming that there's no remainder. + /// + /// # Safety + /// + /// This may only be called when + /// - The slice splits exactly into `N`-element chunks (aka `self.len() % N == 0`). + /// - `N != 0`. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_as_chunks)] + /// let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!']; + /// let chunks: &[[char; 1]] = + /// // SAFETY: 1-element chunks never have remainder + /// unsafe { slice.as_chunks_unchecked() }; + /// assert_eq!(chunks, &[['l'], ['o'], ['r'], ['e'], ['m'], ['!']]); + /// let chunks: &[[char; 3]] = + /// // SAFETY: The slice length (6) is a multiple of 3 + /// unsafe { slice.as_chunks_unchecked() }; + /// assert_eq!(chunks, &[['l', 'o', 'r'], ['e', 'm', '!']]); + /// + /// // These would be unsound: + /// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5 + /// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed + /// ``` + #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[inline] + pub unsafe fn as_chunks_unchecked(&self) -> &[[T; N]] { + debug_assert_ne!(N, 0); + debug_assert_eq!(self.len() % N, 0); + let new_len = + // SAFETY: Our precondition is exactly what's needed to call this + unsafe { crate::intrinsics::exact_div(self.len(), N) }; + // SAFETY: We cast a slice of `new_len * N` elements into + // a slice of `new_len` many `N` elements chunks. + unsafe { from_raw_parts(self.as_ptr().cast(), new_len) } + } + /// Splits the slice into a slice of `N`-element arrays, /// starting at the beginning of the slice, /// and a remainder slice with length strictly less than `N`. @@ -908,12 +951,42 @@ impl [T] { assert_ne!(N, 0); let len = self.len() / N; let (multiple_of_n, remainder) = self.split_at(len * N); - // SAFETY: We cast a slice of `len * N` elements into - // a slice of `len` many `N` elements chunks. - let array_slice: &[[T; N]] = unsafe { from_raw_parts(multiple_of_n.as_ptr().cast(), len) }; + // SAFETY: We already panicked for zero, and ensured by construction + // that the length of the subslice is a multiple of N. + let array_slice = unsafe { multiple_of_n.as_chunks_unchecked() }; (array_slice, remainder) } + /// Splits the slice into a slice of `N`-element arrays, + /// starting at the end of the slice, + /// and a remainder slice with length strictly less than `N`. + /// + /// # Panics + /// + /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// error before this method gets stabilized. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_as_chunks)] + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let (remainder, chunks) = slice.as_rchunks(); + /// assert_eq!(remainder, &['l']); + /// assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]); + /// ``` + #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[inline] + pub fn as_rchunks(&self) -> (&[T], &[[T; N]]) { + assert_ne!(N, 0); + let len = self.len() / N; + let (remainder, multiple_of_n) = self.split_at(self.len() - len * N); + // SAFETY: We already panicked for zero, and ensured by construction + // that the length of the subslice is a multiple of N. + let array_slice = unsafe { multiple_of_n.as_chunks_unchecked() }; + (remainder, array_slice) + } + /// Returns an iterator over `N` elements of the slice at a time, starting at the /// beginning of the slice. /// @@ -948,6 +1021,48 @@ impl [T] { ArrayChunks::new(self) } + /// Splits the slice into a slice of `N`-element arrays, + /// assuming that there's no remainder. + /// + /// # Safety + /// + /// This may only be called when + /// - The slice splits exactly into `N`-element chunks (aka `self.len() % N == 0`). + /// - `N != 0`. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_as_chunks)] + /// let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!']; + /// let chunks: &mut [[char; 1]] = + /// // SAFETY: 1-element chunks never have remainder + /// unsafe { slice.as_chunks_unchecked_mut() }; + /// chunks[0] = ['L']; + /// assert_eq!(chunks, &[['L'], ['o'], ['r'], ['e'], ['m'], ['!']]); + /// let chunks: &mut [[char; 3]] = + /// // SAFETY: The slice length (6) is a multiple of 3 + /// unsafe { slice.as_chunks_unchecked_mut() }; + /// chunks[1] = ['a', 'x', '?']; + /// assert_eq!(slice, &['L', 'o', 'r', 'a', 'x', '?']); + /// + /// // These would be unsound: + /// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5 + /// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed + /// ``` + #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[inline] + pub unsafe fn as_chunks_unchecked_mut(&mut self) -> &mut [[T; N]] { + debug_assert_ne!(N, 0); + debug_assert_eq!(self.len() % N, 0); + let new_len = + // SAFETY: Our precondition is exactly what's needed to call this + unsafe { crate::intrinsics::exact_div(self.len(), N) }; + // SAFETY: We cast a slice of `new_len * N` elements into + // a slice of `new_len` many `N` elements chunks. + unsafe { from_raw_parts_mut(self.as_mut_ptr().cast(), new_len) } + } + /// Splits the slice into a slice of `N`-element arrays, /// starting at the beginning of the slice, /// and a remainder slice with length strictly less than `N`. @@ -978,13 +1093,48 @@ impl [T] { assert_ne!(N, 0); let len = self.len() / N; let (multiple_of_n, remainder) = self.split_at_mut(len * N); - let array_slice: &mut [[T; N]] = - // SAFETY: We cast a slice of `len * N` elements into - // a slice of `len` many `N` elements chunks. - unsafe { from_raw_parts_mut(multiple_of_n.as_mut_ptr().cast(), len) }; + // SAFETY: We already panicked for zero, and ensured by construction + // that the length of the subslice is a multiple of N. + let array_slice = unsafe { multiple_of_n.as_chunks_unchecked_mut() }; (array_slice, remainder) } + /// Splits the slice into a slice of `N`-element arrays, + /// starting at the end of the slice, + /// and a remainder slice with length strictly less than `N`. + /// + /// # Panics + /// + /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// error before this method gets stabilized. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_as_chunks)] + /// let v = &mut [0, 0, 0, 0, 0]; + /// let mut count = 1; + /// + /// let (remainder, chunks) = v.as_rchunks_mut(); + /// remainder[0] = 9; + /// for chunk in chunks { + /// *chunk = [count; 2]; + /// count += 1; + /// } + /// assert_eq!(v, &[9, 1, 1, 2, 2]); + /// ``` + #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[inline] + pub fn as_rchunks_mut(&mut self) -> (&mut [T], &mut [[T; N]]) { + assert_ne!(N, 0); + let len = self.len() / N; + let (remainder, multiple_of_n) = self.split_at_mut(self.len() - len * N); + // SAFETY: We already panicked for zero, and ensured by construction + // that the length of the subslice is a multiple of N. + let array_slice = unsafe { multiple_of_n.as_chunks_unchecked_mut() }; + (remainder, array_slice) + } + /// Returns an iterator over `N` elements of the slice at a time, starting at the /// beginning of the slice. /// @@ -1207,6 +1357,96 @@ impl [T] { RChunksExactMut::new(self, chunk_size) } + /// Returns an iterator over the slice producing non-overlapping runs + /// of elements using the predicate to separate them. + /// + /// The predicate is called on two elements following themselves, + /// it means the predicate is called on `slice[0]` and `slice[1]` + /// then on `slice[1]` and `slice[2]` and so on. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_group_by)] + /// + /// let slice = &[1, 1, 1, 3, 3, 2, 2, 2]; + /// + /// let mut iter = slice.group_by(|a, b| a == b); + /// + /// assert_eq!(iter.next(), Some(&[1, 1, 1][..])); + /// assert_eq!(iter.next(), Some(&[3, 3][..])); + /// assert_eq!(iter.next(), Some(&[2, 2, 2][..])); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// This method can be used to extract the sorted subslices: + /// + /// ``` + /// #![feature(slice_group_by)] + /// + /// let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4]; + /// + /// let mut iter = slice.group_by(|a, b| a <= b); + /// + /// assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..])); + /// assert_eq!(iter.next(), Some(&[2, 3][..])); + /// assert_eq!(iter.next(), Some(&[2, 3, 4][..])); + /// assert_eq!(iter.next(), None); + /// ``` + #[unstable(feature = "slice_group_by", issue = "80552")] + #[inline] + pub fn group_by(&self, pred: F) -> GroupBy<'_, T, F> + where + F: FnMut(&T, &T) -> bool, + { + GroupBy::new(self, pred) + } + + /// Returns an iterator over the slice producing non-overlapping mutable + /// runs of elements using the predicate to separate them. + /// + /// The predicate is called on two elements following themselves, + /// it means the predicate is called on `slice[0]` and `slice[1]` + /// then on `slice[1]` and `slice[2]` and so on. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_group_by)] + /// + /// let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2]; + /// + /// let mut iter = slice.group_by_mut(|a, b| a == b); + /// + /// assert_eq!(iter.next(), Some(&mut [1, 1, 1][..])); + /// assert_eq!(iter.next(), Some(&mut [3, 3][..])); + /// assert_eq!(iter.next(), Some(&mut [2, 2, 2][..])); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// This method can be used to extract the sorted subslices: + /// + /// ``` + /// #![feature(slice_group_by)] + /// + /// let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4]; + /// + /// let mut iter = slice.group_by_mut(|a, b| a <= b); + /// + /// assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..])); + /// assert_eq!(iter.next(), Some(&mut [2, 3][..])); + /// assert_eq!(iter.next(), Some(&mut [2, 3, 4][..])); + /// assert_eq!(iter.next(), None); + /// ``` + #[unstable(feature = "slice_group_by", issue = "80552")] + #[inline] + pub fn group_by_mut(&mut self, pred: F) -> GroupByMut<'_, T, F> + where + F: FnMut(&T, &T) -> bool, + { + GroupByMut::new(self, pred) + } + /// Divides one slice into two at an index. /// /// The first will contain all indices from `[0, mid)` (excluding @@ -1263,14 +1503,11 @@ impl [T] { /// /// ``` /// let mut v = [1, 0, 3, 0, 5, 6]; - /// // scoped to restrict the lifetime of the borrows - /// { - /// let (left, right) = v.split_at_mut(2); - /// assert_eq!(left, [1, 0]); - /// assert_eq!(right, [3, 0, 5, 6]); - /// left[1] = 2; - /// right[1] = 4; - /// } + /// let (left, right) = v.split_at_mut(2); + /// assert_eq!(left, [1, 0]); + /// assert_eq!(right, [3, 0, 5, 6]); + /// left[1] = 2; + /// right[1] = 4; /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -1455,7 +1692,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(split_inclusive)] /// let slice = [10, 40, 33, 20]; /// let mut iter = slice.split_inclusive(|num| num % 3 == 0); /// @@ -1469,7 +1705,6 @@ impl [T] { /// That slice will be the last item returned by the iterator. /// /// ``` - /// #![feature(split_inclusive)] /// let slice = [3, 10, 40, 33]; /// let mut iter = slice.split_inclusive(|num| num % 3 == 0); /// @@ -1477,7 +1712,7 @@ impl [T] { /// assert_eq!(iter.next().unwrap(), &[10, 40, 33]); /// assert!(iter.next().is_none()); /// ``` - #[unstable(feature = "split_inclusive", issue = "72360")] + #[stable(feature = "split_inclusive", since = "1.51.0")] #[inline] pub fn split_inclusive(&self, pred: F) -> SplitInclusive<'_, T, F> where @@ -1493,7 +1728,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(split_inclusive)] /// let mut v = [10, 40, 30, 20, 60, 50]; /// /// for group in v.split_inclusive_mut(|num| *num % 3 == 0) { @@ -1502,7 +1736,7 @@ impl [T] { /// } /// assert_eq!(v, [10, 40, 1, 20, 1, 1]); /// ``` - #[unstable(feature = "split_inclusive", issue = "72360")] + #[stable(feature = "split_inclusive", since = "1.51.0")] #[inline] pub fn split_inclusive_mut(&mut self, pred: F) -> SplitInclusiveMut<'_, T, F> where @@ -1778,19 +2012,24 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_strip)] /// let v = &[10, 40, 30]; /// assert_eq!(v.strip_prefix(&[10]), Some(&[40, 30][..])); /// assert_eq!(v.strip_prefix(&[10, 40]), Some(&[30][..])); /// assert_eq!(v.strip_prefix(&[50]), None); /// assert_eq!(v.strip_prefix(&[10, 50]), None); + /// + /// let prefix : &str = "he"; + /// assert_eq!(b"hello".strip_prefix(prefix.as_bytes()), + /// Some(b"llo".as_ref())); /// ``` #[must_use = "returns the subslice without modifying the original"] - #[unstable(feature = "slice_strip", issue = "73413")] - pub fn strip_prefix(&self, prefix: &[T]) -> Option<&[T]> + #[stable(feature = "slice_strip", since = "1.51.0")] + pub fn strip_prefix + ?Sized>(&self, prefix: &P) -> Option<&[T]> where T: PartialEq, { + // This function will need rewriting if and when SlicePattern becomes more sophisticated. + let prefix = prefix.as_slice(); let n = prefix.len(); if n <= self.len() { let (head, tail) = self.split_at(n); @@ -1811,7 +2050,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_strip)] /// let v = &[10, 40, 30]; /// assert_eq!(v.strip_suffix(&[30]), Some(&[10, 40][..])); /// assert_eq!(v.strip_suffix(&[40, 30]), Some(&[10][..])); @@ -1819,11 +2057,13 @@ impl [T] { /// assert_eq!(v.strip_suffix(&[50, 30]), None); /// ``` #[must_use = "returns the subslice without modifying the original"] - #[unstable(feature = "slice_strip", issue = "73413")] - pub fn strip_suffix(&self, suffix: &[T]) -> Option<&[T]> + #[stable(feature = "slice_strip", since = "1.51.0")] + pub fn strip_suffix + ?Sized>(&self, suffix: &P) -> Option<&[T]> where T: PartialEq, { + // This function will need rewriting if and when SlicePattern becomes more sophisticated. + let suffix = suffix.as_slice(); let (len, n) = (self.len(), suffix.len()); if n <= len { let (head, tail) = self.split_at(len - n); @@ -2612,13 +2852,12 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_fill_with)] - /// /// let mut buf = vec![1; 10]; /// buf.fill_with(Default::default); /// assert_eq!(buf, vec![0; 10]); /// ``` - #[unstable(feature = "slice_fill_with", issue = "79221")] + #[doc(alias = "memset")] + #[stable(feature = "slice_fill_with", since = "1.51.0")] pub fn fill_with(&mut self, mut f: F) where F: FnMut() -> T, @@ -3216,3 +3455,35 @@ impl Default for &mut [T] { &mut [] } } + +#[unstable(feature = "slice_pattern", reason = "stopgap trait for slice patterns", issue = "56345")] +/// Patterns in slices - currently, only used by `strip_prefix` and `strip_suffix`. At a future +/// point, we hope to generalise `core::str::Pattern` (which at the time of writing is limited to +/// `str`) to slices, and then this trait will be replaced or abolished. +pub trait SlicePattern { + /// The element type of the slice being matched on. + type Item; + + /// Currently, the consumers of `SlicePattern` need a slice. + fn as_slice(&self) -> &[Self::Item]; +} + +#[stable(feature = "slice_strip", since = "1.51.0")] +impl SlicePattern for [T] { + type Item = T; + + #[inline] + fn as_slice(&self) -> &[Self::Item] { + self + } +} + +#[stable(feature = "slice_strip", since = "1.51.0")] +impl SlicePattern for [T; N] { + type Item = T; + + #[inline] + fn as_slice(&self) -> &[Self::Item] { + self + } +} diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 09209306c9..117db6e3e3 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -103,7 +103,7 @@ pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `data` must be [valid] for boths reads and writes for `len * mem::size_of::()` many bytes, +/// * `data` must be [valid] for both reads and writes for `len * mem::size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// /// * The entire memory range of this slice must be contained within a single allocated object! diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 28cd350019..83f484dc57 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -47,12 +47,7 @@ impl<'a> Iterator for Chars<'a> { #[inline] fn count(self) -> usize { // length in `char` is equal to the number of non-continuation bytes - let bytes_len = self.iter.len(); - let mut cont_bytes = 0; - for &byte in self.iter { - cont_bytes += utf8_is_cont_byte(byte) as usize; - } - bytes_len - cont_bytes + self.iter.filter(|&&byte| !utf8_is_cont_byte(byte)).count() } #[inline] @@ -1174,7 +1169,7 @@ pub struct SplitAsciiWhitespace<'a> { /// See its documentation for more. /// /// [`split_inclusive`]: str::split_inclusive -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] pub struct SplitInclusive<'a, P: Pattern<'a>>(pub(super) SplitInternal<'a, P>); #[stable(feature = "split_whitespace", since = "1.1.0")] @@ -1239,7 +1234,7 @@ impl<'a> DoubleEndedIterator for SplitAsciiWhitespace<'a> { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] impl FusedIterator for SplitAsciiWhitespace<'_> {} -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { type Item = &'a str; @@ -1249,7 +1244,7 @@ impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl<'a, P: Pattern<'a, Searcher: fmt::Debug>> fmt::Debug for SplitInclusive<'a, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitInclusive").field("0", &self.0).finish() @@ -1257,14 +1252,14 @@ impl<'a, P: Pattern<'a, Searcher: fmt::Debug>> fmt::Debug for SplitInclusive<'a, } // FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl<'a, P: Pattern<'a, Searcher: Clone>> Clone for SplitInclusive<'a, P> { fn clone(&self) -> Self { SplitInclusive(self.0.clone()) } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl<'a, P: Pattern<'a, Searcher: ReverseSearcher<'a>>> DoubleEndedIterator for SplitInclusive<'a, P> { @@ -1274,7 +1269,7 @@ impl<'a, P: Pattern<'a, Searcher: ReverseSearcher<'a>>> DoubleEndedIterator } } -#[unstable(feature = "split_inclusive", issue = "72360")] +#[stable(feature = "split_inclusive", since = "1.51.0")] impl<'a, P: Pattern<'a>> FusedIterator for SplitInclusive<'a, P> {} impl<'a, P: Pattern<'a>> SplitInclusive<'a, P> { @@ -1284,7 +1279,6 @@ impl<'a, P: Pattern<'a>> SplitInclusive<'a, P> { /// /// ``` /// #![feature(str_split_inclusive_as_str)] - /// #![feature(split_inclusive)] /// let mut split = "Mary had a little lamb".split_inclusive(' '); /// assert_eq!(split.as_str(), "Mary had a little lamb"); /// split.next(); diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 83cf47c8c8..b8c5a1d03b 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -65,8 +65,8 @@ pub use iter::{EscapeDebug, EscapeDefault, EscapeUnicode}; #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] pub use iter::SplitAsciiWhitespace; -#[unstable(feature = "split_inclusive", issue = "72360")] -use iter::SplitInclusive; +#[stable(feature = "split_inclusive", since = "1.51.0")] +pub use iter::SplitInclusive; #[unstable(feature = "str_internals", issue = "none")] pub use validations::next_code_point; @@ -138,6 +138,7 @@ impl str { /// assert_eq!("ƒoo".len(), 4); // fancy f! /// assert_eq!("ƒoo".chars().count(), 3); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_len", since = "1.32.0")] #[inline] @@ -1226,7 +1227,6 @@ impl str { /// # Examples /// /// ``` - /// #![feature(split_inclusive)] /// let v: Vec<&str> = "Mary had a little lamb\nlittle lamb\nlittle lamb." /// .split_inclusive('\n').collect(); /// assert_eq!(v, ["Mary had a little lamb\n", "little lamb\n", "little lamb."]); @@ -1237,12 +1237,11 @@ impl str { /// That substring will be the last item returned by the iterator. /// /// ``` - /// #![feature(split_inclusive)] /// let v: Vec<&str> = "Mary had a little lamb\nlittle lamb\nlittle lamb.\n" /// .split_inclusive('\n').collect(); /// assert_eq!(v, ["Mary had a little lamb\n", "little lamb\n", "little lamb.\n"]); /// ``` - #[unstable(feature = "split_inclusive", issue = "72360")] + #[stable(feature = "split_inclusive", since = "1.51.0")] #[inline] pub fn split_inclusive<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitInclusive<'a, P> { SplitInclusive(SplitInternal { @@ -2174,7 +2173,7 @@ impl str { /// helps the inference algorithm understand specifically which type /// you're trying to parse into. /// - /// `parse` can parse any type that implements the [`FromStr`] trait. + /// `parse` can parse into any type that implements the [`FromStr`] trait. /// /// # Errors diff --git a/library/core/src/stream/mod.rs b/library/core/src/stream/mod.rs new file mode 100644 index 0000000000..0df18af65e --- /dev/null +++ b/library/core/src/stream/mod.rs @@ -0,0 +1,127 @@ +//! Composable asynchronous iteration. +//! +//! If futures are asynchronous values, then streams are asynchronous +//! iterators. If you've found yourself with an asynchronous collection of some kind, +//! and needed to perform an operation on the elements of said collection, +//! you'll quickly run into 'streams'. Streams are heavily used in idiomatic +//! asynchronous Rust code, so it's worth becoming familiar with them. +//! +//! Before explaining more, let's talk about how this module is structured: +//! +//! # Organization +//! +//! This module is largely organized by type: +//! +//! * [Traits] are the core portion: these traits define what kind of streams +//! exist and what you can do with them. The methods of these traits are worth +//! putting some extra study time into. +//! * Functions provide some helpful ways to create some basic streams. +//! * Structs are often the return types of the various methods on this +//! module's traits. You'll usually want to look at the method that creates +//! the `struct`, rather than the `struct` itself. For more detail about why, +//! see '[Implementing Stream](#implementing-stream)'. +//! +//! [Traits]: #traits +//! +//! That's it! Let's dig into streams. +//! +//! # Stream +//! +//! The heart and soul of this module is the [`Stream`] trait. The core of +//! [`Stream`] looks like this: +//! +//! ``` +//! # use core::task::{Context, Poll}; +//! # use core::pin::Pin; +//! trait Stream { +//! type Item; +//! fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; +//! } +//! ``` +//! +//! Unlike `Iterator`, `Stream` makes a distinction between the [`poll_next`] +//! method which is used when implementing a `Stream`, and a (to-be-implemented) +//! `next` method which is used when consuming a stream. Consumers of `Stream` +//! only need to consider `next`, which when called, returns a future which +//! yields `Option`. +//! +//! The future returned by `next` will yield `Some(Item)` as long as there are +//! elements, and once they've all been exhausted, will yield `None` to indicate +//! that iteration is finished. If we're waiting on something asynchronous to +//! resolve, the future will wait until the stream is ready to yield again. +//! +//! Individual streams may choose to resume iteration, and so calling `next` +//! again may or may not eventually yield `Some(Item)` again at some point. +//! +//! [`Stream`]'s full definition includes a number of other methods as well, +//! but they are default methods, built on top of [`poll_next`], and so you get +//! them for free. +//! +//! [`Poll`]: super::task::Poll +//! [`poll_next`]: Stream::poll_next +//! +//! # Implementing Stream +//! +//! Creating a stream of your own involves two steps: creating a `struct` to +//! hold the stream's state, and then implementing [`Stream`] for that +//! `struct`. +//! +//! Let's make a stream named `Counter` which counts from `1` to `5`: +//! +//! ```no_run +//! #![feature(async_stream)] +//! # use core::stream::Stream; +//! # use core::task::{Context, Poll}; +//! # use core::pin::Pin; +//! +//! // First, the struct: +//! +//! /// A stream which counts from one to five +//! struct Counter { +//! count: usize, +//! } +//! +//! // we want our count to start at one, so let's add a new() method to help. +//! // This isn't strictly necessary, but is convenient. Note that we start +//! // `count` at zero, we'll see why in `poll_next()`'s implementation below. +//! impl Counter { +//! fn new() -> Counter { +//! Counter { count: 0 } +//! } +//! } +//! +//! // Then, we implement `Stream` for our `Counter`: +//! +//! impl Stream for Counter { +//! // we will be counting with usize +//! type Item = usize; +//! +//! // poll_next() is the only required method +//! fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { +//! // Increment our count. This is why we started at zero. +//! self.count += 1; +//! +//! // Check to see if we've finished counting or not. +//! if self.count < 6 { +//! Poll::Ready(Some(self.count)) +//! } else { +//! Poll::Ready(None) +//! } +//! } +//! } +//! ``` +//! +//! # Laziness +//! +//! Streams are *lazy*. This means that just creating a stream doesn't _do_ a +//! whole lot. Nothing really happens until you call `next`. This is sometimes a +//! source of confusion when creating a stream solely for its side effects. The +//! compiler will warn us about this kind of behavior: +//! +//! ```text +//! warning: unused result that must be used: streams do nothing unless polled +//! ``` + +mod stream; + +pub use stream::Stream; diff --git a/library/core/src/stream/stream/mod.rs b/library/core/src/stream/stream/mod.rs new file mode 100644 index 0000000000..e37902dae1 --- /dev/null +++ b/library/core/src/stream/stream/mod.rs @@ -0,0 +1,110 @@ +use crate::ops::DerefMut; +use crate::pin::Pin; +use crate::task::{Context, Poll}; + +/// An interface for dealing with asynchronous iterators. +/// +/// This is the main stream trait. For more about the concept of streams +/// generally, please see the [module-level documentation]. In particular, you +/// may want to know how to [implement `Stream`][impl]. +/// +/// [module-level documentation]: index.html +/// [impl]: index.html#implementing-stream +#[unstable(feature = "async_stream", issue = "79024")] +#[must_use = "streams do nothing unless polled"] +pub trait Stream { + /// The type of items yielded by the stream. + type Item; + + /// Attempt to pull out the next value of this stream, registering the + /// current task for wakeup if the value is not yet available, and returning + /// `None` if the stream is exhausted. + /// + /// # Return value + /// + /// There are several possible return values, each indicating a distinct + /// stream state: + /// + /// - `Poll::Pending` means that this stream's next value is not ready + /// yet. Implementations will ensure that the current task will be notified + /// when the next value may be ready. + /// + /// - `Poll::Ready(Some(val))` means that the stream has successfully + /// produced a value, `val`, and may produce further values on subsequent + /// `poll_next` calls. + /// + /// - `Poll::Ready(None)` means that the stream has terminated, and + /// `poll_next` should not be invoked again. + /// + /// # Panics + /// + /// Once a stream has finished (returned `Ready(None)` from `poll_next`), calling its + /// `poll_next` method again may panic, block forever, or cause other kinds of + /// problems; the `Stream` trait places no requirements on the effects of + /// such a call. However, as the `poll_next` method is not marked `unsafe`, + /// Rust's usual rules apply: calls must never cause undefined behavior + /// (memory corruption, incorrect use of `unsafe` functions, or the like), + /// regardless of the stream's state. + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + + /// Returns the bounds on the remaining length of the stream. + /// + /// Specifically, `size_hint()` returns a tuple where the first element + /// is the lower bound, and the second element is the upper bound. + /// + /// The second half of the tuple that is returned is an [`Option`]`<`[`usize`]`>`. + /// A [`None`] here means that either there is no known upper bound, or the + /// upper bound is larger than [`usize`]. + /// + /// # Implementation notes + /// + /// It is not enforced that a stream implementation yields the declared + /// number of elements. A buggy stream may yield less than the lower bound + /// or more than the upper bound of elements. + /// + /// `size_hint()` is primarily intended to be used for optimizations such as + /// reserving space for the elements of the stream, but must not be + /// trusted to e.g., omit bounds checks in unsafe code. An incorrect + /// implementation of `size_hint()` should not lead to memory safety + /// violations. + /// + /// That said, the implementation should provide a correct estimation, + /// because otherwise it would be a violation of the trait's protocol. + /// + /// The default implementation returns `(0, `[`None`]`)` which is correct for any + /// stream. + #[inline] + fn size_hint(&self) -> (usize, Option) { + (0, None) + } +} + +#[unstable(feature = "async_stream", issue = "79024")] +impl Stream for &mut S { + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + S::poll_next(Pin::new(&mut **self), cx) + } + + fn size_hint(&self) -> (usize, Option) { + (**self).size_hint() + } +} + +#[unstable(feature = "async_stream", issue = "79024")] +impl

Stream for Pin

+where + P: DerefMut + Unpin, + P::Target: Stream, +{ + type Item = ::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.get_mut().as_mut().poll_next(cx) + } + + fn size_hint(&self) -> (usize, Option) { + (**self).size_hint() + } +} diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 36857979af..81c9e1d1c1 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -120,21 +120,6 @@ use crate::intrinsics; use crate::hint::spin_loop; -/// Signals the processor that it is inside a busy-wait spin-loop ("spin lock"). -/// -/// This function is expected to be deprecated in favor of -/// [`hint::spin_loop`]. -/// -/// **Note**: On platforms that do not support receiving spin-loop hints this function does not -/// do anything at all. -/// -/// [`hint::spin_loop`]: crate::hint::spin_loop -#[inline] -#[stable(feature = "spin_loop_hint", since = "1.24.0")] -pub fn spin_loop_hint() { - spin_loop() -} - /// A boolean type which can be safely shared between threads. /// /// This type has the same in-memory representation as a [`bool`]. @@ -809,7 +794,7 @@ impl AtomicBool { /// ```ignore (extern-declaration) /// # fn main() { /// use std::sync::atomic::AtomicBool; - /// extern { + /// extern "C" { /// fn my_atomic_op(arg: *mut bool); /// } /// @@ -991,16 +976,8 @@ impl AtomicPtr { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn load(&self, order: Ordering) -> *mut T { - #[cfg(not(bootstrap))] // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_load(self.p.get(), order) - } - #[cfg(bootstrap)] - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_load(self.p.get() as *mut usize, order) as *mut T - } + unsafe { atomic_load(self.p.get(), order) } } /// Stores a value into the pointer. @@ -1027,16 +1004,10 @@ impl AtomicPtr { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn store(&self, ptr: *mut T, order: Ordering) { - #[cfg(not(bootstrap))] // SAFETY: data races are prevented by atomic intrinsics. unsafe { atomic_store(self.p.get(), ptr, order); } - #[cfg(bootstrap)] - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_store(self.p.get() as *mut usize, ptr as usize, order); - } } /// Stores a value into the pointer, returning the previous value. @@ -1065,16 +1036,8 @@ impl AtomicPtr { #[stable(feature = "rust1", since = "1.0.0")] #[cfg(target_has_atomic = "ptr")] pub fn swap(&self, ptr: *mut T, order: Ordering) -> *mut T { - #[cfg(bootstrap)] // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_swap(self.p.get() as *mut usize, ptr as usize, order) as *mut T - } - #[cfg(not(bootstrap))] - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_swap(self.p.get(), ptr, order) - } + unsafe { atomic_swap(self.p.get(), ptr, order) } } /// Stores a value into the pointer if the current value is the same as the `current` value. @@ -1174,26 +1137,8 @@ impl AtomicPtr { success: Ordering, failure: Ordering, ) -> Result<*mut T, *mut T> { - #[cfg(bootstrap)] - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - let res = atomic_compare_exchange( - self.p.get() as *mut usize, - current as usize, - new as usize, - success, - failure, - ); - match res { - Ok(x) => Ok(x as *mut T), - Err(x) => Err(x as *mut T), - } - } - #[cfg(not(bootstrap))] // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_compare_exchange(self.p.get(), current, new, success, failure) - } + unsafe { atomic_compare_exchange(self.p.get(), current, new, success, failure) } } /// Stores a value into the pointer if the current value is the same as the `current` value. @@ -1241,29 +1186,11 @@ impl AtomicPtr { success: Ordering, failure: Ordering, ) -> Result<*mut T, *mut T> { - #[cfg(bootstrap)] - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - let res = atomic_compare_exchange_weak( - self.p.get() as *mut usize, - current as usize, - new as usize, - success, - failure, - ); - match res { - Ok(x) => Ok(x as *mut T), - Err(x) => Err(x as *mut T), - } - } - #[cfg(not(bootstrap))] // SAFETY: This intrinsic is unsafe because it operates on a raw pointer // but we know for sure that the pointer is valid (we just got it from // an `UnsafeCell` that we have by reference) and the atomic operation // itself allows us to safely mutate the `UnsafeCell` contents. - unsafe { - atomic_compare_exchange_weak(self.p.get(), current, new, success, failure) - } + unsafe { atomic_compare_exchange_weak(self.p.get(), current, new, success, failure) } } /// Fetches the value, and applies a function to it that returns an optional @@ -1430,12 +1357,9 @@ macro_rules! atomic_int { #[$stable_from] impl From<$int_type> for $atomic_type { - doc_comment! { - concat!( -"Converts an `", stringify!($int_type), "` into an `", stringify!($atomic_type), "`."), - #[inline] - fn from(v: $int_type) -> Self { Self::new(v) } - } + #[doc = concat!("Converts an `", stringify!($int_type), "` into an `", stringify!($atomic_type), "`.")] + #[inline] + fn from(v: $int_type) -> Self { Self::new(v) } } #[$stable_debug] @@ -1450,744 +1374,703 @@ macro_rules! atomic_int { unsafe impl Sync for $atomic_type {} impl $atomic_type { - doc_comment! { - concat!("Creates a new atomic integer. - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::", stringify!($atomic_type), "; - -let atomic_forty_two = ", stringify!($atomic_type), "::new(42); -```"), - #[inline] - #[$stable] - #[$const_stable] - pub const fn new(v: $int_type) -> Self { - Self {v: UnsafeCell::new(v)} - } + /// Creates a new atomic integer. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::", stringify!($atomic_type), ";")] + /// + #[doc = concat!("let atomic_forty_two = ", stringify!($atomic_type), "::new(42);")] + /// ``` + #[inline] + #[$stable] + #[$const_stable] + pub const fn new(v: $int_type) -> Self { + Self {v: UnsafeCell::new(v)} } - doc_comment! { - concat!("Returns a mutable reference to the underlying integer. - -This is safe because the mutable reference guarantees that no other threads are -concurrently accessing the atomic data. - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let mut some_var = ", stringify!($atomic_type), "::new(10); -assert_eq!(*some_var.get_mut(), 10); -*some_var.get_mut() = 5; -assert_eq!(some_var.load(Ordering::SeqCst), 5); -```"), - #[inline] - #[$stable_access] - pub fn get_mut(&mut self) -> &mut $int_type { - self.v.get_mut() - } + /// Returns a mutable reference to the underlying integer. + /// + /// This is safe because the mutable reference guarantees that no other threads are + /// concurrently accessing the atomic data. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let mut some_var = ", stringify!($atomic_type), "::new(10);")] + /// assert_eq!(*some_var.get_mut(), 10); + /// *some_var.get_mut() = 5; + /// assert_eq!(some_var.load(Ordering::SeqCst), 5); + /// ``` + #[inline] + #[$stable_access] + pub fn get_mut(&mut self) -> &mut $int_type { + self.v.get_mut() } - doc_comment! { - concat!("Get atomic access to a `&mut ", stringify!($int_type), "`. - -", -if_not_8_bit! { - $int_type, - concat!( - "**Note:** This function is only available on targets where `", - stringify!($int_type), "` has an alignment of ", $align, " bytes." - ) -}, -" - -# Examples - -``` -#![feature(atomic_from_mut)] -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let mut some_int = 123; -let a = ", stringify!($atomic_type), "::from_mut(&mut some_int); -a.store(100, Ordering::Relaxed); -assert_eq!(some_int, 100); -``` - "), - #[inline] - #[$cfg_align] - #[unstable(feature = "atomic_from_mut", issue = "76314")] - pub fn from_mut(v: &mut $int_type) -> &Self { - use crate::mem::align_of; - let [] = [(); align_of::() - align_of::<$int_type>()]; - // SAFETY: - // - the mutable reference guarantees unique ownership. - // - the alignment of `$int_type` and `Self` is the - // same, as promised by $cfg_align and verified above. - unsafe { &*(v as *mut $int_type as *mut Self) } - } + #[doc = concat!("Get atomic access to a `&mut ", stringify!($int_type), "`.")] + /// + #[doc = if_not_8_bit! { + $int_type, + concat!( + "**Note:** This function is only available on targets where `", + stringify!($int_type), "` has an alignment of ", $align, " bytes." + ) + }] + /// + /// # Examples + /// + /// ``` + /// #![feature(atomic_from_mut)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + /// let mut some_int = 123; + #[doc = concat!("let a = ", stringify!($atomic_type), "::from_mut(&mut some_int);")] + /// a.store(100, Ordering::Relaxed); + /// assert_eq!(some_int, 100); + /// ``` + /// + #[inline] + #[$cfg_align] + #[unstable(feature = "atomic_from_mut", issue = "76314")] + pub fn from_mut(v: &mut $int_type) -> &Self { + use crate::mem::align_of; + let [] = [(); align_of::() - align_of::<$int_type>()]; + // SAFETY: + // - the mutable reference guarantees unique ownership. + // - the alignment of `$int_type` and `Self` is the + // same, as promised by $cfg_align and verified above. + unsafe { &*(v as *mut $int_type as *mut Self) } } - doc_comment! { - concat!("Consumes the atomic and returns the contained value. - -This is safe because passing `self` by value guarantees that no other threads are -concurrently accessing the atomic data. - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::", stringify!($atomic_type), "; - -let some_var = ", stringify!($atomic_type), "::new(5); -assert_eq!(some_var.into_inner(), 5); -```"), - #[inline] - #[$stable_access] - #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] - pub const fn into_inner(self) -> $int_type { - self.v.into_inner() - } + /// Consumes the atomic and returns the contained value. + /// + /// This is safe because passing `self` by value guarantees that no other threads are + /// concurrently accessing the atomic data. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::", stringify!($atomic_type), ";")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// assert_eq!(some_var.into_inner(), 5); + /// ``` + #[inline] + #[$stable_access] + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> $int_type { + self.v.into_inner() } - doc_comment! { - concat!("Loads a value from the atomic integer. - -`load` takes an [`Ordering`] argument which describes the memory ordering of this operation. -Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. - -# Panics - -Panics if `order` is [`Release`] or [`AcqRel`]. - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let some_var = ", stringify!($atomic_type), "::new(5); - -assert_eq!(some_var.load(Ordering::Relaxed), 5); -```"), - #[inline] - #[$stable] - pub fn load(&self, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_load(self.v.get(), order) } - } + /// Loads a value from the atomic integer. + /// + /// `load` takes an [`Ordering`] argument which describes the memory ordering of this operation. + /// Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. + /// + /// # Panics + /// + /// Panics if `order` is [`Release`] or [`AcqRel`]. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// + /// assert_eq!(some_var.load(Ordering::Relaxed), 5); + /// ``` + #[inline] + #[$stable] + pub fn load(&self, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_load(self.v.get(), order) } } - doc_comment! { - concat!("Stores a value into the atomic integer. - -`store` takes an [`Ordering`] argument which describes the memory ordering of this operation. - Possible values are [`SeqCst`], [`Release`] and [`Relaxed`]. - -# Panics - -Panics if `order` is [`Acquire`] or [`AcqRel`]. - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let some_var = ", stringify!($atomic_type), "::new(5); - -some_var.store(10, Ordering::Relaxed); -assert_eq!(some_var.load(Ordering::Relaxed), 10); -```"), - #[inline] - #[$stable] - pub fn store(&self, val: $int_type, order: Ordering) { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_store(self.v.get(), val, order); } - } + /// Stores a value into the atomic integer. + /// + /// `store` takes an [`Ordering`] argument which describes the memory ordering of this operation. + /// Possible values are [`SeqCst`], [`Release`] and [`Relaxed`]. + /// + /// # Panics + /// + /// Panics if `order` is [`Acquire`] or [`AcqRel`]. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// + /// some_var.store(10, Ordering::Relaxed); + /// assert_eq!(some_var.load(Ordering::Relaxed), 10); + /// ``` + #[inline] + #[$stable] + pub fn store(&self, val: $int_type, order: Ordering) { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_store(self.v.get(), val, order); } } - doc_comment! { - concat!("Stores a value into the atomic integer, returning the previous value. - -`swap` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let some_var = ", stringify!($atomic_type), "::new(5); - -assert_eq!(some_var.swap(10, Ordering::Relaxed), 5); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_swap(self.v.get(), val, order) } - } + /// Stores a value into the atomic integer, returning the previous value. + /// + /// `swap` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// + /// assert_eq!(some_var.swap(10, Ordering::Relaxed), 5); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_swap(self.v.get(), val, order) } } - doc_comment! { - concat!("Stores a value into the atomic integer if the current value is the same as -the `current` value. - -The return value is always the previous value. If it is equal to `current`, then the -value was updated. - -`compare_and_swap` also takes an [`Ordering`] argument which describes the memory -ordering of this operation. Notice that even when using [`AcqRel`], the operation -might fail and hence just perform an `Acquire` load, but not have `Release` semantics. -Using [`Acquire`] makes the store part of this operation [`Relaxed`] if it -happens, and using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Migrating to `compare_exchange` and `compare_exchange_weak` - -`compare_and_swap` is equivalent to `compare_exchange` with the following mapping for -memory orderings: - -Original | Success | Failure --------- | ------- | ------- -Relaxed | Relaxed | Relaxed -Acquire | Acquire | Acquire -Release | Release | Relaxed -AcqRel | AcqRel | Acquire -SeqCst | SeqCst | SeqCst - -`compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, -which allows the compiler to generate better assembly code when the compare and swap -is used in a loop. - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let some_var = ", stringify!($atomic_type), "::new(5); - -assert_eq!(some_var.compare_and_swap(5, 10, Ordering::Relaxed), 5); -assert_eq!(some_var.load(Ordering::Relaxed), 10); - -assert_eq!(some_var.compare_and_swap(6, 12, Ordering::Relaxed), 10); -assert_eq!(some_var.load(Ordering::Relaxed), 10); -```"), - #[inline] - #[$stable] - #[rustc_deprecated( - since = "1.50.0", - reason = "Use `compare_exchange` or `compare_exchange_weak` instead") - ] - #[$cfg_cas] - pub fn compare_and_swap(&self, - current: $int_type, - new: $int_type, - order: Ordering) -> $int_type { - match self.compare_exchange(current, - new, - order, - strongest_failure_ordering(order)) { - Ok(x) => x, - Err(x) => x, - } + /// Stores a value into the atomic integer if the current value is the same as + /// the `current` value. + /// + /// The return value is always the previous value. If it is equal to `current`, then the + /// value was updated. + /// + /// `compare_and_swap` also takes an [`Ordering`] argument which describes the memory + /// ordering of this operation. Notice that even when using [`AcqRel`], the operation + /// might fail and hence just perform an `Acquire` load, but not have `Release` semantics. + /// Using [`Acquire`] makes the store part of this operation [`Relaxed`] if it + /// happens, and using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Migrating to `compare_exchange` and `compare_exchange_weak` + /// + /// `compare_and_swap` is equivalent to `compare_exchange` with the following mapping for + /// memory orderings: + /// + /// Original | Success | Failure + /// -------- | ------- | ------- + /// Relaxed | Relaxed | Relaxed + /// Acquire | Acquire | Acquire + /// Release | Release | Relaxed + /// AcqRel | AcqRel | Acquire + /// SeqCst | SeqCst | SeqCst + /// + /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, + /// which allows the compiler to generate better assembly code when the compare and swap + /// is used in a loop. + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// + /// assert_eq!(some_var.compare_and_swap(5, 10, Ordering::Relaxed), 5); + /// assert_eq!(some_var.load(Ordering::Relaxed), 10); + /// + /// assert_eq!(some_var.compare_and_swap(6, 12, Ordering::Relaxed), 10); + /// assert_eq!(some_var.load(Ordering::Relaxed), 10); + /// ``` + #[inline] + #[$stable] + #[rustc_deprecated( + since = "1.50.0", + reason = "Use `compare_exchange` or `compare_exchange_weak` instead") + ] + #[$cfg_cas] + pub fn compare_and_swap(&self, + current: $int_type, + new: $int_type, + order: Ordering) -> $int_type { + match self.compare_exchange(current, + new, + order, + strongest_failure_ordering(order)) { + Ok(x) => x, + Err(x) => x, } } - doc_comment! { - concat!("Stores a value into the atomic integer if the current value is the same as -the `current` value. - -The return value is a result indicating whether the new value was written and -containing the previous value. On success this value is guaranteed to be equal to -`current`. - -`compare_exchange` takes two [`Ordering`] arguments to describe the memory -ordering of this operation. `success` describes the required ordering for the -read-modify-write operation that takes place if the comparison with `current` succeeds. -`failure` describes the required ordering for the load operation that takes place when -the comparison fails. Using [`Acquire`] as success ordering makes the store part -of this operation [`Relaxed`], and using [`Release`] makes the successful load -[`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] -and must be equivalent to or weaker than the success ordering. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let some_var = ", stringify!($atomic_type), "::new(5); - -assert_eq!(some_var.compare_exchange(5, 10, - Ordering::Acquire, - Ordering::Relaxed), - Ok(5)); -assert_eq!(some_var.load(Ordering::Relaxed), 10); - -assert_eq!(some_var.compare_exchange(6, 12, - Ordering::SeqCst, - Ordering::Acquire), - Err(10)); -assert_eq!(some_var.load(Ordering::Relaxed), 10); -```"), - #[inline] - #[$stable_cxchg] - #[$cfg_cas] - pub fn compare_exchange(&self, - current: $int_type, - new: $int_type, - success: Ordering, - failure: Ordering) -> Result<$int_type, $int_type> { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_compare_exchange(self.v.get(), current, new, success, failure) } - } + /// Stores a value into the atomic integer if the current value is the same as + /// the `current` value. + /// + /// The return value is a result indicating whether the new value was written and + /// containing the previous value. On success this value is guaranteed to be equal to + /// `current`. + /// + /// `compare_exchange` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the successful load + /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] + /// and must be equivalent to or weaker than the success ordering. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let some_var = ", stringify!($atomic_type), "::new(5);")] + /// + /// assert_eq!(some_var.compare_exchange(5, 10, + /// Ordering::Acquire, + /// Ordering::Relaxed), + /// Ok(5)); + /// assert_eq!(some_var.load(Ordering::Relaxed), 10); + /// + /// assert_eq!(some_var.compare_exchange(6, 12, + /// Ordering::SeqCst, + /// Ordering::Acquire), + /// Err(10)); + /// assert_eq!(some_var.load(Ordering::Relaxed), 10); + /// ``` + #[inline] + #[$stable_cxchg] + #[$cfg_cas] + pub fn compare_exchange(&self, + current: $int_type, + new: $int_type, + success: Ordering, + failure: Ordering) -> Result<$int_type, $int_type> { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_compare_exchange(self.v.get(), current, new, success, failure) } } - doc_comment! { - concat!("Stores a value into the atomic integer if the current value is the same as -the `current` value. - -Unlike [`", stringify!($atomic_type), "::compare_exchange`], this function is allowed to spuriously fail even -when the comparison succeeds, which can result in more efficient code on some -platforms. The return value is a result indicating whether the new value was -written and containing the previous value. - -`compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory -ordering of this operation. `success` describes the required ordering for the -read-modify-write operation that takes place if the comparison with `current` succeeds. -`failure` describes the required ordering for the load operation that takes place when -the comparison fails. Using [`Acquire`] as success ordering makes the store part -of this operation [`Relaxed`], and using [`Release`] makes the successful load -[`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] -and must be equivalent to or weaker than the success ordering. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let val = ", stringify!($atomic_type), "::new(4); - -let mut old = val.load(Ordering::Relaxed); -loop { - let new = old * 2; - match val.compare_exchange_weak(old, new, Ordering::SeqCst, Ordering::Relaxed) { - Ok(_) => break, - Err(x) => old = x, - } -} -```"), - #[inline] - #[$stable_cxchg] - #[$cfg_cas] - pub fn compare_exchange_weak(&self, - current: $int_type, - new: $int_type, - success: Ordering, - failure: Ordering) -> Result<$int_type, $int_type> { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { - atomic_compare_exchange_weak(self.v.get(), current, new, success, failure) - } + /// Stores a value into the atomic integer if the current value is the same as + /// the `current` value. + /// + #[doc = concat!("Unlike [`", stringify!($atomic_type), "::compare_exchange`],")] + /// this function is allowed to spuriously fail even + /// when the comparison succeeds, which can result in more efficient code on some + /// platforms. The return value is a result indicating whether the new value was + /// written and containing the previous value. + /// + /// `compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. `success` describes the required ordering for the + /// read-modify-write operation that takes place if the comparison with `current` succeeds. + /// `failure` describes the required ordering for the load operation that takes place when + /// the comparison fails. Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the successful load + /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] + /// and must be equivalent to or weaker than the success ordering. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let val = ", stringify!($atomic_type), "::new(4);")] + /// + /// let mut old = val.load(Ordering::Relaxed); + /// loop { + /// let new = old * 2; + /// match val.compare_exchange_weak(old, new, Ordering::SeqCst, Ordering::Relaxed) { + /// Ok(_) => break, + /// Err(x) => old = x, + /// } + /// } + /// ``` + #[inline] + #[$stable_cxchg] + #[$cfg_cas] + pub fn compare_exchange_weak(&self, + current: $int_type, + new: $int_type, + success: Ordering, + failure: Ordering) -> Result<$int_type, $int_type> { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { + atomic_compare_exchange_weak(self.v.get(), current, new, success, failure) } } - doc_comment! { - concat!("Adds to the current value, returning the previous value. - -This operation wraps around on overflow. - -`fetch_add` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(0); -assert_eq!(foo.fetch_add(10, Ordering::SeqCst), 0); -assert_eq!(foo.load(Ordering::SeqCst), 10); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_add(self.v.get(), val, order) } - } + /// Adds to the current value, returning the previous value. + /// + /// This operation wraps around on overflow. + /// + /// `fetch_add` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(0);")] + /// assert_eq!(foo.fetch_add(10, Ordering::SeqCst), 0); + /// assert_eq!(foo.load(Ordering::SeqCst), 10); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_add(self.v.get(), val, order) } } - doc_comment! { - concat!("Subtracts from the current value, returning the previous value. - -This operation wraps around on overflow. - -`fetch_sub` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(20); -assert_eq!(foo.fetch_sub(10, Ordering::SeqCst), 20); -assert_eq!(foo.load(Ordering::SeqCst), 10); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_sub(self.v.get(), val, order) } - } + /// Subtracts from the current value, returning the previous value. + /// + /// This operation wraps around on overflow. + /// + /// `fetch_sub` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(20);")] + /// assert_eq!(foo.fetch_sub(10, Ordering::SeqCst), 20); + /// assert_eq!(foo.load(Ordering::SeqCst), 10); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_sub(self.v.get(), val, order) } } - doc_comment! { - concat!("Bitwise \"and\" with the current value. - -Performs a bitwise \"and\" operation on the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_and` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(0b101101); -assert_eq!(foo.fetch_and(0b110011, Ordering::SeqCst), 0b101101); -assert_eq!(foo.load(Ordering::SeqCst), 0b100001); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_and(self.v.get(), val, order) } - } + /// Bitwise "and" with the current value. + /// + /// Performs a bitwise "and" operation on the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_and` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(0b101101);")] + /// assert_eq!(foo.fetch_and(0b110011, Ordering::SeqCst), 0b101101); + /// assert_eq!(foo.load(Ordering::SeqCst), 0b100001); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_and(self.v.get(), val, order) } } - doc_comment! { - concat!("Bitwise \"nand\" with the current value. - -Performs a bitwise \"nand\" operation on the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_nand` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, " -use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(0x13); -assert_eq!(foo.fetch_nand(0x31, Ordering::SeqCst), 0x13); -assert_eq!(foo.load(Ordering::SeqCst), !(0x13 & 0x31)); -```"), - #[inline] - #[$stable_nand] - #[$cfg_cas] - pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_nand(self.v.get(), val, order) } - } + /// Bitwise "nand" with the current value. + /// + /// Performs a bitwise "nand" operation on the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_nand` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(0x13);")] + /// assert_eq!(foo.fetch_nand(0x31, Ordering::SeqCst), 0x13); + /// assert_eq!(foo.load(Ordering::SeqCst), !(0x13 & 0x31)); + /// ``` + #[inline] + #[$stable_nand] + #[$cfg_cas] + pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_nand(self.v.get(), val, order) } } - doc_comment! { - concat!("Bitwise \"or\" with the current value. - -Performs a bitwise \"or\" operation on the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_or` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(0b101101); -assert_eq!(foo.fetch_or(0b110011, Ordering::SeqCst), 0b101101); -assert_eq!(foo.load(Ordering::SeqCst), 0b111111); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_or(self.v.get(), val, order) } - } + /// Bitwise "or" with the current value. + /// + /// Performs a bitwise "or" operation on the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_or` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(0b101101);")] + /// assert_eq!(foo.fetch_or(0b110011, Ordering::SeqCst), 0b101101); + /// assert_eq!(foo.load(Ordering::SeqCst), 0b111111); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_or(self.v.get(), val, order) } } - doc_comment! { - concat!("Bitwise \"xor\" with the current value. - -Performs a bitwise \"xor\" operation on the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_xor` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(0b101101); -assert_eq!(foo.fetch_xor(0b110011, Ordering::SeqCst), 0b101101); -assert_eq!(foo.load(Ordering::SeqCst), 0b011110); -```"), - #[inline] - #[$stable] - #[$cfg_cas] - pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_xor(self.v.get(), val, order) } - } + /// Bitwise "xor" with the current value. + /// + /// Performs a bitwise "xor" operation on the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_xor` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(0b101101);")] + /// assert_eq!(foo.fetch_xor(0b110011, Ordering::SeqCst), 0b101101); + /// assert_eq!(foo.load(Ordering::SeqCst), 0b011110); + /// ``` + #[inline] + #[$stable] + #[$cfg_cas] + pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_xor(self.v.get(), val, order) } } - doc_comment! { - concat!("Fetches the value, and applies a function to it that returns an optional -new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else -`Err(previous_value)`. - -Note: This may call the function multiple times if the value has been changed from other threads in -the meantime, as long as the function returns `Some(_)`, but the function will have been applied -only once to the stored value. - -`fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. -The first describes the required ordering for when the operation finally succeeds while the second -describes the required ordering for loads. These correspond to the success and failure orderings of -[`", stringify!($atomic_type), "::compare_exchange`] respectively. - -Using [`Acquire`] as success ordering makes the store part -of this operation [`Relaxed`], and using [`Release`] makes the final successful load -[`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] -and must be equivalent to or weaker than the success ordering. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -```rust -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let x = ", stringify!($atomic_type), "::new(7); -assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); -assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); -assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); -assert_eq!(x.load(Ordering::SeqCst), 9); -```"), - #[inline] - #[stable(feature = "no_more_cas", since = "1.45.0")] - #[$cfg_cas] - pub fn fetch_update(&self, - set_order: Ordering, - fetch_order: Ordering, - mut f: F) -> Result<$int_type, $int_type> - where F: FnMut($int_type) -> Option<$int_type> { - let mut prev = self.load(fetch_order); - while let Some(next) = f(prev) { - match self.compare_exchange_weak(prev, next, set_order, fetch_order) { - x @ Ok(_) => return x, - Err(next_prev) => prev = next_prev - } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else + /// `Err(previous_value)`. + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied + /// only once to the stored value. + /// + /// `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] + /// and must be equivalent to or weaker than the success ordering. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ```rust + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); + /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); + /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[stable(feature = "no_more_cas", since = "1.45.0")] + #[$cfg_cas] + pub fn fetch_update(&self, + set_order: Ordering, + fetch_order: Ordering, + mut f: F) -> Result<$int_type, $int_type> + where F: FnMut($int_type) -> Option<$int_type> { + let mut prev = self.load(fetch_order); + while let Some(next) = f(prev) { + match self.compare_exchange_weak(prev, next, set_order, fetch_order) { + x @ Ok(_) => return x, + Err(next_prev) => prev = next_prev } - Err(prev) } + Err(prev) } - doc_comment! { - concat!("Maximum with the current value. - -Finds the maximum of the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_max` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(23); -assert_eq!(foo.fetch_max(42, Ordering::SeqCst), 23); -assert_eq!(foo.load(Ordering::SeqCst), 42); -``` - -If you want to obtain the maximum value in one step, you can use the following: - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(23); -let bar = 42; -let max_foo = foo.fetch_max(bar, Ordering::SeqCst).max(bar); -assert!(max_foo == 42); -```"), - #[inline] - #[stable(feature = "atomic_min_max", since = "1.45.0")] - #[$cfg_cas] - pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { $max_fn(self.v.get(), val, order) } - } + /// Maximum with the current value. + /// + /// Finds the maximum of the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_max` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(23);")] + /// assert_eq!(foo.fetch_max(42, Ordering::SeqCst), 23); + /// assert_eq!(foo.load(Ordering::SeqCst), 42); + /// ``` + /// + /// If you want to obtain the maximum value in one step, you can use the following: + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(23);")] + /// let bar = 42; + /// let max_foo = foo.fetch_max(bar, Ordering::SeqCst).max(bar); + /// assert!(max_foo == 42); + /// ``` + #[inline] + #[stable(feature = "atomic_min_max", since = "1.45.0")] + #[$cfg_cas] + pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { $max_fn(self.v.get(), val, order) } } - doc_comment! { - concat!("Minimum with the current value. - -Finds the minimum of the current value and the argument `val`, and -sets the new value to the result. - -Returns the previous value. - -`fetch_min` takes an [`Ordering`] argument which describes the memory ordering -of this operation. All ordering modes are possible. Note that using -[`Acquire`] makes the store part of this operation [`Relaxed`], and -using [`Release`] makes the load part [`Relaxed`]. - -**Note**: This method is only available on platforms that support atomic -operations on [`", $s_int_type, "`](", $int_ref, "). - -# Examples - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(23); -assert_eq!(foo.fetch_min(42, Ordering::Relaxed), 23); -assert_eq!(foo.load(Ordering::Relaxed), 23); -assert_eq!(foo.fetch_min(22, Ordering::Relaxed), 23); -assert_eq!(foo.load(Ordering::Relaxed), 22); -``` - -If you want to obtain the minimum value in one step, you can use the following: - -``` -", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; - -let foo = ", stringify!($atomic_type), "::new(23); -let bar = 12; -let min_foo = foo.fetch_min(bar, Ordering::SeqCst).min(bar); -assert_eq!(min_foo, 12); -```"), - #[inline] - #[stable(feature = "atomic_min_max", since = "1.45.0")] - #[$cfg_cas] - pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { $min_fn(self.v.get(), val, order) } - } + /// Minimum with the current value. + /// + /// Finds the minimum of the current value and the argument `val`, and + /// sets the new value to the result. + /// + /// Returns the previous value. + /// + /// `fetch_min` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. All ordering modes are possible. Note that using + /// [`Acquire`] makes the store part of this operation [`Relaxed`], and + /// using [`Release`] makes the load part [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`](", $int_ref, ").")] + /// + /// # Examples + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(23);")] + /// assert_eq!(foo.fetch_min(42, Ordering::Relaxed), 23); + /// assert_eq!(foo.load(Ordering::Relaxed), 23); + /// assert_eq!(foo.fetch_min(22, Ordering::Relaxed), 23); + /// assert_eq!(foo.load(Ordering::Relaxed), 22); + /// ``` + /// + /// If you want to obtain the minimum value in one step, you can use the following: + /// + /// ``` + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let foo = ", stringify!($atomic_type), "::new(23);")] + /// let bar = 12; + /// let min_foo = foo.fetch_min(bar, Ordering::SeqCst).min(bar); + /// assert_eq!(min_foo, 12); + /// ``` + #[inline] + #[stable(feature = "atomic_min_max", since = "1.45.0")] + #[$cfg_cas] + pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { $min_fn(self.v.get(), val, order) } } - doc_comment! { - concat!("Returns a mutable pointer to the underlying integer. - -Doing non-atomic reads and writes on the resulting integer can be a data race. -This method is mostly useful for FFI, where the function signature may use -`*mut ", stringify!($int_type), "` instead of `&", stringify!($atomic_type), "`. - -Returning an `*mut` pointer from a shared reference to this atomic is safe because the -atomic types work with interior mutability. All modifications of an atomic change the value -through a shared reference, and can do so safely as long as they use atomic operations. Any -use of the returned raw pointer requires an `unsafe` block and still has to uphold the same -restriction: operations on it must be atomic. - -# Examples - -```ignore (extern-declaration) -# fn main() { -", $extra_feature, "use std::sync::atomic::", stringify!($atomic_type), "; - -extern { - fn my_atomic_op(arg: *mut ", stringify!($int_type), "); -} - -let mut atomic = ", stringify!($atomic_type), "::new(1); -", -// SAFETY: Safe as long as `my_atomic_op` is atomic. -"unsafe { - my_atomic_op(atomic.as_mut_ptr()); -} -# } -```"), - #[inline] - #[unstable(feature = "atomic_mut_ptr", - reason = "recently added", - issue = "66893")] - pub fn as_mut_ptr(&self) -> *mut $int_type { - self.v.get() - } + /// Returns a mutable pointer to the underlying integer. + /// + /// Doing non-atomic reads and writes on the resulting integer can be a data race. + /// This method is mostly useful for FFI, where the function signature may use + #[doc = concat!("`*mut ", stringify!($int_type), "` instead of `&", stringify!($atomic_type), "`.")] + /// + /// Returning an `*mut` pointer from a shared reference to this atomic is safe because the + /// atomic types work with interior mutability. All modifications of an atomic change the value + /// through a shared reference, and can do so safely as long as they use atomic operations. Any + /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same + /// restriction: operations on it must be atomic. + /// + /// # Examples + /// + /// ```ignore (extern-declaration) + /// # fn main() { + #[doc = concat!($extra_feature, "use std::sync::atomic::", stringify!($atomic_type), ";")] + /// + /// extern "C" { + #[doc = concat!(" fn my_atomic_op(arg: *mut ", stringify!($int_type), ");")] + /// } + /// + #[doc = concat!("let mut atomic = ", stringify!($atomic_type), "::new(1);")] + /// + // SAFETY: Safe as long as `my_atomic_op` is atomic. + /// unsafe { + /// my_atomic_op(atomic.as_mut_ptr()); + /// } + /// # } + /// ``` + #[inline] + #[unstable(feature = "atomic_mut_ptr", + reason = "recently added", + issue = "66893")] + pub fn as_mut_ptr(&self) -> *mut $int_type { + self.v.get() } } } @@ -2893,3 +2776,15 @@ impl fmt::Pointer for AtomicPtr { fmt::Pointer::fmt(&self.load(Ordering::SeqCst), f) } } + +/// Signals the processor that it is inside a busy-wait spin-loop ("spin lock"). +/// +/// This function is deprecated in favor of [`hint::spin_loop`]. +/// +/// [`hint::spin_loop`]: crate::hint::spin_loop +#[inline] +#[stable(feature = "spin_loop_hint", since = "1.24.0")] +#[rustc_deprecated(since = "1.51.0", reason = "use hint::spin_loop instead")] +pub fn spin_loop_hint() { + spin_loop() +} diff --git a/library/core/src/task/poll.rs b/library/core/src/task/poll.rs index 6851f3fcd2..42c9d9f0cc 100644 --- a/library/core/src/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -84,7 +84,7 @@ impl Poll> { impl Poll>> { /// Changes the success value of this `Poll` with the closure provided. - #[unstable(feature = "poll_map", issue = "63514")] + #[stable(feature = "poll_map", since = "1.51.0")] pub fn map_ok(self, f: F) -> Poll>> where F: FnOnce(T) -> U, @@ -98,7 +98,7 @@ impl Poll>> { } /// Changes the error value of this `Poll` with the closure provided. - #[unstable(feature = "poll_map", issue = "63514")] + #[stable(feature = "poll_map", since = "1.51.0")] pub fn map_err(self, f: F) -> Poll>> where F: FnOnce(E) -> U, diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 88b4e2a243..b1443bc33d 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -1067,13 +1067,23 @@ impl fmt::Debug for Duration { } if self.secs > 0 { - fmt_decimal(f, self.secs, self.nanos, 100_000_000)?; + fmt_decimal(f, self.secs, self.nanos, NANOS_PER_SEC / 10)?; f.write_str("s") - } else if self.nanos >= 1_000_000 { - fmt_decimal(f, self.nanos as u64 / 1_000_000, self.nanos % 1_000_000, 100_000)?; + } else if self.nanos >= NANOS_PER_MILLI { + fmt_decimal( + f, + (self.nanos / NANOS_PER_MILLI) as u64, + self.nanos % NANOS_PER_MILLI, + NANOS_PER_MILLI / 10, + )?; f.write_str("ms") - } else if self.nanos >= 1_000 { - fmt_decimal(f, self.nanos as u64 / 1_000, self.nanos % 1_000, 100)?; + } else if self.nanos >= NANOS_PER_MICRO { + fmt_decimal( + f, + (self.nanos / NANOS_PER_MICRO) as u64, + self.nanos % NANOS_PER_MICRO, + NANOS_PER_MICRO / 10, + )?; f.write_str("µs") } else { fmt_decimal(f, self.nanos as u64, 0, 1)?; diff --git a/library/core/tests/const_ptr.rs b/library/core/tests/const_ptr.rs new file mode 100644 index 0000000000..4acd059ab0 --- /dev/null +++ b/library/core/tests/const_ptr.rs @@ -0,0 +1,51 @@ +// Aligned to two bytes +const DATA: [u16; 2] = [u16::from_ne_bytes([0x01, 0x23]), u16::from_ne_bytes([0x45, 0x67])]; + +const fn unaligned_ptr() -> *const u16 { + // Since DATA.as_ptr() is aligned to two bytes, adding 1 byte to that produces an unaligned *const u16 + unsafe { (DATA.as_ptr() as *const u8).add(1) as *const u16 } +} + +#[test] +fn read() { + use core::ptr; + + const FOO: i32 = unsafe { ptr::read(&42 as *const i32) }; + assert_eq!(FOO, 42); + + const ALIGNED: i32 = unsafe { ptr::read_unaligned(&42 as *const i32) }; + assert_eq!(ALIGNED, 42); + + const UNALIGNED_PTR: *const u16 = unaligned_ptr(); + + const UNALIGNED: u16 = unsafe { ptr::read_unaligned(UNALIGNED_PTR) }; + assert_eq!(UNALIGNED, u16::from_ne_bytes([0x23, 0x45])); +} + +#[test] +fn const_ptr_read() { + const FOO: i32 = unsafe { (&42 as *const i32).read() }; + assert_eq!(FOO, 42); + + const ALIGNED: i32 = unsafe { (&42 as *const i32).read_unaligned() }; + assert_eq!(ALIGNED, 42); + + const UNALIGNED_PTR: *const u16 = unaligned_ptr(); + + const UNALIGNED: u16 = unsafe { UNALIGNED_PTR.read_unaligned() }; + assert_eq!(UNALIGNED, u16::from_ne_bytes([0x23, 0x45])); +} + +#[test] +fn mut_ptr_read() { + const FOO: i32 = unsafe { (&42 as *const i32 as *mut i32).read() }; + assert_eq!(FOO, 42); + + const ALIGNED: i32 = unsafe { (&42 as *const i32 as *mut i32).read_unaligned() }; + assert_eq!(ALIGNED, 42); + + const UNALIGNED_PTR: *mut u16 = unaligned_ptr() as *mut u16; + + const UNALIGNED: u16 = unsafe { UNALIGNED_PTR.read_unaligned() }; + assert_eq!(UNALIGNED, u16::from_ne_bytes([0x23, 0x45])); +} diff --git a/library/core/tests/iter.rs b/library/core/tests/iter.rs deleted file mode 100644 index ec4b49da38..0000000000 --- a/library/core/tests/iter.rs +++ /dev/null @@ -1,3507 +0,0 @@ -// ignore-tidy-filelength - -use core::cell::Cell; -use core::convert::TryFrom; -use core::iter::*; - -/// An iterator wrapper that panics whenever `next` or `next_back` is called -/// after `None` has been returned. -struct Unfuse { - iter: I, - exhausted: bool, -} - -fn unfuse(iter: I) -> Unfuse { - Unfuse { iter: iter.into_iter(), exhausted: false } -} - -impl Iterator for Unfuse -where - I: Iterator, -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - assert!(!self.exhausted); - let next = self.iter.next(); - self.exhausted = next.is_none(); - next - } -} - -impl DoubleEndedIterator for Unfuse -where - I: DoubleEndedIterator, -{ - fn next_back(&mut self) -> Option { - assert!(!self.exhausted); - let next = self.iter.next_back(); - self.exhausted = next.is_none(); - next - } -} - -#[test] -fn test_lt() { - let empty: [isize; 0] = []; - let xs = [1, 2, 3]; - let ys = [1, 2, 0]; - - assert!(!xs.iter().lt(ys.iter())); - assert!(!xs.iter().le(ys.iter())); - assert!(xs.iter().gt(ys.iter())); - assert!(xs.iter().ge(ys.iter())); - - assert!(ys.iter().lt(xs.iter())); - assert!(ys.iter().le(xs.iter())); - assert!(!ys.iter().gt(xs.iter())); - assert!(!ys.iter().ge(xs.iter())); - - assert!(empty.iter().lt(xs.iter())); - assert!(empty.iter().le(xs.iter())); - assert!(!empty.iter().gt(xs.iter())); - assert!(!empty.iter().ge(xs.iter())); - - // Sequence with NaN - let u = [1.0f64, 2.0]; - let v = [0.0f64 / 0.0, 3.0]; - - assert!(!u.iter().lt(v.iter())); - assert!(!u.iter().le(v.iter())); - assert!(!u.iter().gt(v.iter())); - assert!(!u.iter().ge(v.iter())); - - let a = [0.0f64 / 0.0]; - let b = [1.0f64]; - let c = [2.0f64]; - - assert!(a.iter().lt(b.iter()) == (a[0] < b[0])); - assert!(a.iter().le(b.iter()) == (a[0] <= b[0])); - assert!(a.iter().gt(b.iter()) == (a[0] > b[0])); - assert!(a.iter().ge(b.iter()) == (a[0] >= b[0])); - - assert!(c.iter().lt(b.iter()) == (c[0] < b[0])); - assert!(c.iter().le(b.iter()) == (c[0] <= b[0])); - assert!(c.iter().gt(b.iter()) == (c[0] > b[0])); - assert!(c.iter().ge(b.iter()) == (c[0] >= b[0])); -} - -#[test] -fn test_multi_iter() { - let xs = [1, 2, 3, 4]; - let ys = [4, 3, 2, 1]; - assert!(xs.iter().eq(ys.iter().rev())); - assert!(xs.iter().lt(xs.iter().skip(2))); -} - -#[test] -fn test_cmp_by() { - use core::cmp::Ordering; - - let f = |x: i32, y: i32| (x * x).cmp(&y); - let xs = || [1, 2, 3, 4].iter().copied(); - let ys = || [1, 4, 16].iter().copied(); - - assert_eq!(xs().cmp_by(ys(), f), Ordering::Less); - assert_eq!(ys().cmp_by(xs(), f), Ordering::Greater); - assert_eq!(xs().cmp_by(xs().map(|x| x * x), f), Ordering::Equal); - assert_eq!(xs().rev().cmp_by(ys().rev(), f), Ordering::Greater); - assert_eq!(xs().cmp_by(ys().rev(), f), Ordering::Less); - assert_eq!(xs().cmp_by(ys().take(2), f), Ordering::Greater); -} - -#[test] -fn test_partial_cmp_by() { - use core::cmp::Ordering; - - let f = |x: i32, y: i32| (x * x).partial_cmp(&y); - let xs = || [1, 2, 3, 4].iter().copied(); - let ys = || [1, 4, 16].iter().copied(); - - assert_eq!(xs().partial_cmp_by(ys(), f), Some(Ordering::Less)); - assert_eq!(ys().partial_cmp_by(xs(), f), Some(Ordering::Greater)); - assert_eq!(xs().partial_cmp_by(xs().map(|x| x * x), f), Some(Ordering::Equal)); - assert_eq!(xs().rev().partial_cmp_by(ys().rev(), f), Some(Ordering::Greater)); - assert_eq!(xs().partial_cmp_by(xs().rev(), f), Some(Ordering::Less)); - assert_eq!(xs().partial_cmp_by(ys().take(2), f), Some(Ordering::Greater)); - - let f = |x: f64, y: f64| (x * x).partial_cmp(&y); - let xs = || [1.0, 2.0, 3.0, 4.0].iter().copied(); - let ys = || [1.0, 4.0, f64::NAN, 16.0].iter().copied(); - - assert_eq!(xs().partial_cmp_by(ys(), f), None); - assert_eq!(ys().partial_cmp_by(xs(), f), Some(Ordering::Greater)); -} - -#[test] -fn test_eq_by() { - let f = |x: i32, y: i32| x * x == y; - let xs = || [1, 2, 3, 4].iter().copied(); - let ys = || [1, 4, 9, 16].iter().copied(); - - assert!(xs().eq_by(ys(), f)); - assert!(!ys().eq_by(xs(), f)); - assert!(!xs().eq_by(xs(), f)); - assert!(!ys().eq_by(ys(), f)); - - assert!(!xs().take(3).eq_by(ys(), f)); - assert!(!xs().eq_by(ys().take(3), f)); - assert!(xs().take(3).eq_by(ys().take(3), f)); -} - -#[test] -fn test_counter_from_iter() { - let it = (0..).step_by(5).take(10); - let xs: Vec = FromIterator::from_iter(it); - assert_eq!(xs, [0, 5, 10, 15, 20, 25, 30, 35, 40, 45]); -} - -#[test] -fn test_iterator_chain() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; - let it = xs.iter().chain(&ys); - let mut i = 0; - for &x in it { - assert_eq!(x, expected[i]); - i += 1; - } - assert_eq!(i, expected.len()); - - let ys = (30..).step_by(10).take(4); - let it = xs.iter().cloned().chain(ys); - let mut i = 0; - for x in it { - assert_eq!(x, expected[i]); - i += 1; - } - assert_eq!(i, expected.len()); -} - -#[test] -fn test_iterator_chain_advance_by() { - fn test_chain(xs: &[i32], ys: &[i32]) { - let len = xs.len() + ys.len(); - - for i in 0..xs.len() { - let mut iter = unfuse(xs).chain(unfuse(ys)); - iter.advance_by(i).unwrap(); - assert_eq!(iter.next(), Some(&xs[i])); - assert_eq!(iter.advance_by(100), Err(len - i - 1)); - } - - for i in 0..ys.len() { - let mut iter = unfuse(xs).chain(unfuse(ys)); - iter.advance_by(xs.len() + i).unwrap(); - assert_eq!(iter.next(), Some(&ys[i])); - assert_eq!(iter.advance_by(100), Err(ys.len() - i - 1)); - } - - let mut iter = xs.iter().chain(ys); - iter.advance_by(len).unwrap(); - assert_eq!(iter.next(), None); - - let mut iter = xs.iter().chain(ys); - assert_eq!(iter.advance_by(len + 1), Err(len)); - } - - test_chain(&[], &[]); - test_chain(&[], &[0, 1, 2, 3, 4, 5]); - test_chain(&[0, 1, 2, 3, 4, 5], &[]); - test_chain(&[0, 1, 2, 3, 4, 5], &[30, 40, 50, 60]); -} - -#[test] -fn test_iterator_chain_advance_back_by() { - fn test_chain(xs: &[i32], ys: &[i32]) { - let len = xs.len() + ys.len(); - - for i in 0..ys.len() { - let mut iter = unfuse(xs).chain(unfuse(ys)); - iter.advance_back_by(i).unwrap(); - assert_eq!(iter.next_back(), Some(&ys[ys.len() - i - 1])); - assert_eq!(iter.advance_back_by(100), Err(len - i - 1)); - } - - for i in 0..xs.len() { - let mut iter = unfuse(xs).chain(unfuse(ys)); - iter.advance_back_by(ys.len() + i).unwrap(); - assert_eq!(iter.next_back(), Some(&xs[xs.len() - i - 1])); - assert_eq!(iter.advance_back_by(100), Err(xs.len() - i - 1)); - } - - let mut iter = xs.iter().chain(ys); - iter.advance_back_by(len).unwrap(); - assert_eq!(iter.next_back(), None); - - let mut iter = xs.iter().chain(ys); - assert_eq!(iter.advance_back_by(len + 1), Err(len)); - } - - test_chain(&[], &[]); - test_chain(&[], &[0, 1, 2, 3, 4, 5]); - test_chain(&[0, 1, 2, 3, 4, 5], &[]); - test_chain(&[0, 1, 2, 3, 4, 5], &[30, 40, 50, 60]); -} - -#[test] -fn test_iterator_chain_nth() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let zs = []; - let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; - for (i, x) in expected.iter().enumerate() { - assert_eq!(Some(x), xs.iter().chain(&ys).nth(i)); - } - assert_eq!(zs.iter().chain(&xs).nth(0), Some(&0)); - - let mut it = xs.iter().chain(&zs); - assert_eq!(it.nth(5), Some(&5)); - assert_eq!(it.next(), None); -} - -#[test] -fn test_iterator_chain_nth_back() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let zs = []; - let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; - for (i, x) in expected.iter().rev().enumerate() { - assert_eq!(Some(x), xs.iter().chain(&ys).nth_back(i)); - } - assert_eq!(zs.iter().chain(&xs).nth_back(0), Some(&5)); - - let mut it = xs.iter().chain(&zs); - assert_eq!(it.nth_back(5), Some(&0)); - assert_eq!(it.next(), None); -} - -#[test] -fn test_iterator_chain_last() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let zs = []; - assert_eq!(xs.iter().chain(&ys).last(), Some(&60)); - assert_eq!(zs.iter().chain(&ys).last(), Some(&60)); - assert_eq!(ys.iter().chain(&zs).last(), Some(&60)); - assert_eq!(zs.iter().chain(&zs).last(), None); -} - -#[test] -fn test_iterator_chain_count() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let zs = []; - assert_eq!(xs.iter().chain(&ys).count(), 10); - assert_eq!(zs.iter().chain(&ys).count(), 4); -} - -#[test] -fn test_iterator_chain_find() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [30, 40, 50, 60]; - let mut iter = xs.iter().chain(&ys); - assert_eq!(iter.find(|&&i| i == 4), Some(&4)); - assert_eq!(iter.next(), Some(&5)); - assert_eq!(iter.find(|&&i| i == 40), Some(&40)); - assert_eq!(iter.next(), Some(&50)); - assert_eq!(iter.find(|&&i| i == 100), None); - assert_eq!(iter.next(), None); -} - -struct Toggle { - is_empty: bool, -} - -impl Iterator for Toggle { - type Item = (); - - // alternates between `None` and `Some(())` - fn next(&mut self) -> Option { - if self.is_empty { - self.is_empty = false; - None - } else { - self.is_empty = true; - Some(()) - } - } - - fn size_hint(&self) -> (usize, Option) { - if self.is_empty { (0, Some(0)) } else { (1, Some(1)) } - } -} - -impl DoubleEndedIterator for Toggle { - fn next_back(&mut self) -> Option { - self.next() - } -} - -#[test] -fn test_iterator_chain_size_hint() { - // this chains an iterator of length 0 with an iterator of length 1, - // so after calling `.next()` once, the iterator is empty and the - // state is `ChainState::Back`. `.size_hint()` should now disregard - // the size hint of the left iterator - let mut iter = Toggle { is_empty: true }.chain(once(())); - assert_eq!(iter.next(), Some(())); - assert_eq!(iter.size_hint(), (0, Some(0))); - - let mut iter = once(()).chain(Toggle { is_empty: true }); - assert_eq!(iter.next_back(), Some(())); - assert_eq!(iter.size_hint(), (0, Some(0))); -} - -#[test] -fn test_iterator_chain_unfused() { - // Chain shouldn't be fused in its second iterator, depending on direction - let mut iter = NonFused::new(empty()).chain(Toggle { is_empty: true }); - iter.next().unwrap_none(); - iter.next().unwrap(); - iter.next().unwrap_none(); - - let mut iter = Toggle { is_empty: true }.chain(NonFused::new(empty())); - iter.next_back().unwrap_none(); - iter.next_back().unwrap(); - iter.next_back().unwrap_none(); -} - -#[test] -fn test_zip_nth() { - let xs = [0, 1, 2, 4, 5]; - let ys = [10, 11, 12]; - - let mut it = xs.iter().zip(&ys); - assert_eq!(it.nth(0), Some((&0, &10))); - assert_eq!(it.nth(1), Some((&2, &12))); - assert_eq!(it.nth(0), None); - - let mut it = xs.iter().zip(&ys); - assert_eq!(it.nth(3), None); - - let mut it = ys.iter().zip(&xs); - assert_eq!(it.nth(3), None); -} - -#[test] -fn test_zip_nth_side_effects() { - let mut a = Vec::new(); - let mut b = Vec::new(); - let value = [1, 2, 3, 4, 5, 6] - .iter() - .cloned() - .map(|n| { - a.push(n); - n * 10 - }) - .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { - b.push(n * 100); - n * 1000 - })) - .skip(1) - .nth(3); - assert_eq!(value, Some((50, 6000))); - assert_eq!(a, vec![1, 2, 3, 4, 5]); - assert_eq!(b, vec![200, 300, 400, 500, 600]); -} - -#[test] -fn test_zip_next_back_side_effects() { - let mut a = Vec::new(); - let mut b = Vec::new(); - let mut iter = [1, 2, 3, 4, 5, 6] - .iter() - .cloned() - .map(|n| { - a.push(n); - n * 10 - }) - .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { - b.push(n * 100); - n * 1000 - })); - - // The second iterator is one item longer, so `next_back` is called on it - // one more time. - assert_eq!(iter.next_back(), Some((60, 7000))); - assert_eq!(iter.next_back(), Some((50, 6000))); - assert_eq!(iter.next_back(), Some((40, 5000))); - assert_eq!(iter.next_back(), Some((30, 4000))); - assert_eq!(a, vec![6, 5, 4, 3]); - assert_eq!(b, vec![800, 700, 600, 500, 400]); -} - -#[test] -fn test_zip_nth_back_side_effects() { - let mut a = Vec::new(); - let mut b = Vec::new(); - let value = [1, 2, 3, 4, 5, 6] - .iter() - .cloned() - .map(|n| { - a.push(n); - n * 10 - }) - .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { - b.push(n * 100); - n * 1000 - })) - .nth_back(3); - assert_eq!(value, Some((30, 4000))); - assert_eq!(a, vec![6, 5, 4, 3]); - assert_eq!(b, vec![800, 700, 600, 500, 400]); -} - -#[test] -fn test_zip_next_back_side_effects_exhausted() { - let mut a = Vec::new(); - let mut b = Vec::new(); - let mut iter = [1, 2, 3, 4, 5, 6] - .iter() - .cloned() - .map(|n| { - a.push(n); - n * 10 - }) - .zip([2, 3, 4].iter().cloned().map(|n| { - b.push(n * 100); - n * 1000 - })); - - iter.next(); - iter.next(); - iter.next(); - iter.next(); - assert_eq!(iter.next_back(), None); - assert_eq!(a, vec![1, 2, 3, 4, 6, 5]); - assert_eq!(b, vec![200, 300, 400]); -} - -#[derive(Debug)] -struct CountClone(Cell); - -fn count_clone() -> CountClone { - CountClone(Cell::new(0)) -} - -impl PartialEq for CountClone { - fn eq(&self, rhs: &i32) -> bool { - self.0.get() == *rhs - } -} - -impl Clone for CountClone { - fn clone(&self) -> Self { - let ret = CountClone(self.0.clone()); - let n = self.0.get(); - self.0.set(n + 1); - ret - } -} - -#[test] -fn test_zip_cloned_sideffectful() { - let xs = [count_clone(), count_clone(), count_clone(), count_clone()]; - let ys = [count_clone(), count_clone()]; - - for _ in xs.iter().cloned().zip(ys.iter().cloned()) {} - - assert_eq!(&xs, &[1, 1, 1, 0][..]); - assert_eq!(&ys, &[1, 1][..]); - - let xs = [count_clone(), count_clone()]; - let ys = [count_clone(), count_clone(), count_clone(), count_clone()]; - - for _ in xs.iter().cloned().zip(ys.iter().cloned()) {} - - assert_eq!(&xs, &[1, 1][..]); - assert_eq!(&ys, &[1, 1, 0, 0][..]); -} - -#[test] -fn test_zip_map_sideffectful() { - let mut xs = [0; 6]; - let mut ys = [0; 4]; - - for _ in xs.iter_mut().map(|x| *x += 1).zip(ys.iter_mut().map(|y| *y += 1)) {} - - assert_eq!(&xs, &[1, 1, 1, 1, 1, 0]); - assert_eq!(&ys, &[1, 1, 1, 1]); - - let mut xs = [0; 4]; - let mut ys = [0; 6]; - - for _ in xs.iter_mut().map(|x| *x += 1).zip(ys.iter_mut().map(|y| *y += 1)) {} - - assert_eq!(&xs, &[1, 1, 1, 1]); - assert_eq!(&ys, &[1, 1, 1, 1, 0, 0]); -} - -#[test] -fn test_zip_map_rev_sideffectful() { - let mut xs = [0; 6]; - let mut ys = [0; 4]; - - { - let mut it = xs.iter_mut().map(|x| *x += 1).zip(ys.iter_mut().map(|y| *y += 1)); - it.next_back(); - } - assert_eq!(&xs, &[0, 0, 0, 1, 1, 1]); - assert_eq!(&ys, &[0, 0, 0, 1]); - - let mut xs = [0; 6]; - let mut ys = [0; 4]; - - { - let mut it = xs.iter_mut().map(|x| *x += 1).zip(ys.iter_mut().map(|y| *y += 1)); - (&mut it).take(5).count(); - it.next_back(); - } - assert_eq!(&xs, &[1, 1, 1, 1, 1, 1]); - assert_eq!(&ys, &[1, 1, 1, 1]); -} - -#[test] -fn test_zip_nested_sideffectful() { - let mut xs = [0; 6]; - let ys = [0; 4]; - - { - // test that it has the side effect nested inside enumerate - let it = xs.iter_mut().map(|x| *x = 1).enumerate().zip(&ys); - it.count(); - } - assert_eq!(&xs, &[1, 1, 1, 1, 1, 0]); -} - -#[test] -fn test_zip_nth_back_side_effects_exhausted() { - let mut a = Vec::new(); - let mut b = Vec::new(); - let mut iter = [1, 2, 3, 4, 5, 6] - .iter() - .cloned() - .map(|n| { - a.push(n); - n * 10 - }) - .zip([2, 3, 4].iter().cloned().map(|n| { - b.push(n * 100); - n * 1000 - })); - - iter.next(); - iter.next(); - iter.next(); - iter.next(); - assert_eq!(iter.nth_back(0), None); - assert_eq!(a, vec![1, 2, 3, 4, 6, 5]); - assert_eq!(b, vec![200, 300, 400]); -} - -#[test] -fn test_iterator_step_by() { - // Identity - let mut it = (0..).step_by(1).take(3); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next(), Some(1)); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.next(), None); - - let mut it = (0..).step_by(3).take(4); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next(), Some(3)); - assert_eq!(it.next(), Some(6)); - assert_eq!(it.next(), Some(9)); - assert_eq!(it.next(), None); - - let mut it = (0..3).step_by(1); - assert_eq!(it.next_back(), Some(2)); - assert_eq!(it.next_back(), Some(1)); - assert_eq!(it.next_back(), Some(0)); - assert_eq!(it.next_back(), None); - - let mut it = (0..11).step_by(3); - assert_eq!(it.next_back(), Some(9)); - assert_eq!(it.next_back(), Some(6)); - assert_eq!(it.next_back(), Some(3)); - assert_eq!(it.next_back(), Some(0)); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_iterator_step_by_nth() { - let mut it = (0..16).step_by(5); - assert_eq!(it.nth(0), Some(0)); - assert_eq!(it.nth(0), Some(5)); - assert_eq!(it.nth(0), Some(10)); - assert_eq!(it.nth(0), Some(15)); - assert_eq!(it.nth(0), None); - - let it = (0..18).step_by(5); - assert_eq!(it.clone().nth(0), Some(0)); - assert_eq!(it.clone().nth(1), Some(5)); - assert_eq!(it.clone().nth(2), Some(10)); - assert_eq!(it.clone().nth(3), Some(15)); - assert_eq!(it.clone().nth(4), None); - assert_eq!(it.clone().nth(42), None); -} - -#[test] -fn test_iterator_step_by_nth_overflow() { - #[cfg(target_pointer_width = "8")] - type Bigger = u16; - #[cfg(target_pointer_width = "16")] - type Bigger = u32; - #[cfg(target_pointer_width = "32")] - type Bigger = u64; - #[cfg(target_pointer_width = "64")] - type Bigger = u128; - - #[derive(Clone)] - struct Test(Bigger); - impl Iterator for &mut Test { - type Item = i32; - fn next(&mut self) -> Option { - Some(21) - } - fn nth(&mut self, n: usize) -> Option { - self.0 += n as Bigger + 1; - Some(42) - } - } - - let mut it = Test(0); - let root = usize::MAX >> (usize::BITS / 2); - let n = root + 20; - (&mut it).step_by(n).nth(n); - assert_eq!(it.0, n as Bigger * n as Bigger); - - // large step - let mut it = Test(0); - (&mut it).step_by(usize::MAX).nth(5); - assert_eq!(it.0, (usize::MAX as Bigger) * 5); - - // n + 1 overflows - let mut it = Test(0); - (&mut it).step_by(2).nth(usize::MAX); - assert_eq!(it.0, (usize::MAX as Bigger) * 2); - - // n + 1 overflows - let mut it = Test(0); - (&mut it).step_by(1).nth(usize::MAX); - assert_eq!(it.0, (usize::MAX as Bigger) * 1); -} - -#[test] -fn test_iterator_step_by_nth_try_fold() { - let mut it = (0..).step_by(10); - assert_eq!(it.try_fold(0, i8::checked_add), None); - assert_eq!(it.next(), Some(60)); - assert_eq!(it.try_fold(0, i8::checked_add), None); - assert_eq!(it.next(), Some(90)); - - let mut it = (100..).step_by(10); - assert_eq!(it.try_fold(50, i8::checked_add), None); - assert_eq!(it.next(), Some(110)); - - let mut it = (100..=100).step_by(10); - assert_eq!(it.next(), Some(100)); - assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); -} - -#[test] -fn test_iterator_step_by_nth_back() { - let mut it = (0..16).step_by(5); - assert_eq!(it.nth_back(0), Some(15)); - assert_eq!(it.nth_back(0), Some(10)); - assert_eq!(it.nth_back(0), Some(5)); - assert_eq!(it.nth_back(0), Some(0)); - assert_eq!(it.nth_back(0), None); - - let mut it = (0..16).step_by(5); - assert_eq!(it.next(), Some(0)); // to set `first_take` to `false` - assert_eq!(it.nth_back(0), Some(15)); - assert_eq!(it.nth_back(0), Some(10)); - assert_eq!(it.nth_back(0), Some(5)); - assert_eq!(it.nth_back(0), None); - - let it = || (0..18).step_by(5); - assert_eq!(it().nth_back(0), Some(15)); - assert_eq!(it().nth_back(1), Some(10)); - assert_eq!(it().nth_back(2), Some(5)); - assert_eq!(it().nth_back(3), Some(0)); - assert_eq!(it().nth_back(4), None); - assert_eq!(it().nth_back(42), None); -} - -#[test] -fn test_iterator_step_by_nth_try_rfold() { - let mut it = (0..100).step_by(10); - assert_eq!(it.try_rfold(0, i8::checked_add), None); - assert_eq!(it.next_back(), Some(70)); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.try_rfold(0, i8::checked_add), None); - assert_eq!(it.next_back(), Some(30)); - - let mut it = (0..100).step_by(10); - assert_eq!(it.try_rfold(50, i8::checked_add), None); - assert_eq!(it.next_back(), Some(80)); - - let mut it = (100..=100).step_by(10); - assert_eq!(it.next_back(), Some(100)); - assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); -} - -#[test] -#[should_panic] -fn test_iterator_step_by_zero() { - let mut it = (0..).step_by(0); - it.next(); -} - -#[test] -fn test_iterator_step_by_size_hint() { - struct StubSizeHint(usize, Option); - impl Iterator for StubSizeHint { - type Item = (); - fn next(&mut self) -> Option<()> { - self.0 -= 1; - if let Some(ref mut upper) = self.1 { - *upper -= 1; - } - Some(()) - } - fn size_hint(&self) -> (usize, Option) { - (self.0, self.1) - } - } - - // The two checks in each case are needed because the logic - // is different before the first call to `next()`. - - let mut it = StubSizeHint(10, Some(10)).step_by(1); - assert_eq!(it.size_hint(), (10, Some(10))); - it.next(); - assert_eq!(it.size_hint(), (9, Some(9))); - - // exact multiple - let mut it = StubSizeHint(10, Some(10)).step_by(3); - assert_eq!(it.size_hint(), (4, Some(4))); - it.next(); - assert_eq!(it.size_hint(), (3, Some(3))); - - // larger base range, but not enough to get another element - let mut it = StubSizeHint(12, Some(12)).step_by(3); - assert_eq!(it.size_hint(), (4, Some(4))); - it.next(); - assert_eq!(it.size_hint(), (3, Some(3))); - - // smaller base range, so fewer resulting elements - let mut it = StubSizeHint(9, Some(9)).step_by(3); - assert_eq!(it.size_hint(), (3, Some(3))); - it.next(); - assert_eq!(it.size_hint(), (2, Some(2))); - - // infinite upper bound - let mut it = StubSizeHint(usize::MAX, None).step_by(1); - assert_eq!(it.size_hint(), (usize::MAX, None)); - it.next(); - assert_eq!(it.size_hint(), (usize::MAX - 1, None)); - - // still infinite with larger step - let mut it = StubSizeHint(7, None).step_by(3); - assert_eq!(it.size_hint(), (3, None)); - it.next(); - assert_eq!(it.size_hint(), (2, None)); - - // propagates ExactSizeIterator - let a = [1, 2, 3, 4, 5]; - let it = a.iter().step_by(2); - assert_eq!(it.len(), 3); - - // Cannot be TrustedLen as a step greater than one makes an iterator - // with (usize::MAX, None) no longer meet the safety requirements - trait TrustedLenCheck { - fn test(self) -> bool; - } - impl TrustedLenCheck for T { - default fn test(self) -> bool { - false - } - } - impl TrustedLenCheck for T { - fn test(self) -> bool { - true - } - } - assert!(TrustedLenCheck::test(a.iter())); - assert!(!TrustedLenCheck::test(a.iter().step_by(1))); -} - -#[test] -fn test_filter_map() { - let it = (0..).step_by(1).take(10).filter_map(|x| if x % 2 == 0 { Some(x * x) } else { None }); - assert_eq!(it.collect::>(), [0 * 0, 2 * 2, 4 * 4, 6 * 6, 8 * 8]); -} - -#[test] -fn test_filter_map_fold() { - let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let ys = [0 * 0, 2 * 2, 4 * 4, 6 * 6, 8 * 8]; - let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }); - let i = it.fold(0, |i, x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }); - let i = it.rfold(ys.len(), |i, x| { - assert_eq!(x, ys[i - 1]); - i - 1 - }); - assert_eq!(i, 0); -} - -#[test] -fn test_iterator_enumerate() { - let xs = [0, 1, 2, 3, 4, 5]; - let it = xs.iter().enumerate(); - for (i, &x) in it { - assert_eq!(i, x); - } -} - -#[test] -fn test_iterator_enumerate_nth() { - let xs = [0, 1, 2, 3, 4, 5]; - for (i, &x) in xs.iter().enumerate() { - assert_eq!(i, x); - } - - let mut it = xs.iter().enumerate(); - while let Some((i, &x)) = it.nth(0) { - assert_eq!(i, x); - } - - let mut it = xs.iter().enumerate(); - while let Some((i, &x)) = it.nth(1) { - assert_eq!(i, x); - } - - let (i, &x) = xs.iter().enumerate().nth(3).unwrap(); - assert_eq!(i, x); - assert_eq!(i, 3); -} - -#[test] -fn test_iterator_enumerate_nth_back() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().enumerate(); - while let Some((i, &x)) = it.nth_back(0) { - assert_eq!(i, x); - } - - let mut it = xs.iter().enumerate(); - while let Some((i, &x)) = it.nth_back(1) { - assert_eq!(i, x); - } - - let (i, &x) = xs.iter().enumerate().nth_back(3).unwrap(); - assert_eq!(i, x); - assert_eq!(i, 2); -} - -#[test] -fn test_iterator_enumerate_count() { - let xs = [0, 1, 2, 3, 4, 5]; - assert_eq!(xs.iter().enumerate().count(), 6); -} - -#[test] -fn test_iterator_enumerate_fold() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().enumerate(); - // steal a couple to get an interesting offset - assert_eq!(it.next(), Some((0, &0))); - assert_eq!(it.next(), Some((1, &1))); - let i = it.fold(2, |i, (j, &x)| { - assert_eq!(i, j); - assert_eq!(x, xs[j]); - i + 1 - }); - assert_eq!(i, xs.len()); - - let mut it = xs.iter().enumerate(); - assert_eq!(it.next(), Some((0, &0))); - let i = it.rfold(xs.len() - 1, |i, (j, &x)| { - assert_eq!(i, j); - assert_eq!(x, xs[j]); - i - 1 - }); - assert_eq!(i, 0); -} - -#[test] -fn test_iterator_filter_count() { - let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(xs.iter().filter(|&&x| x % 2 == 0).count(), 5); -} - -#[test] -fn test_iterator_filter_fold() { - let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let ys = [0, 2, 4, 6, 8]; - let it = xs.iter().filter(|&&x| x % 2 == 0); - let i = it.fold(0, |i, &x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let it = xs.iter().filter(|&&x| x % 2 == 0); - let i = it.rfold(ys.len(), |i, &x| { - assert_eq!(x, ys[i - 1]); - i - 1 - }); - assert_eq!(i, 0); -} - -#[test] -fn test_iterator_peekable() { - let xs = vec![0, 1, 2, 3, 4, 5]; - - let mut it = xs.iter().cloned().peekable(); - assert_eq!(it.len(), 6); - assert_eq!(it.peek().unwrap(), &0); - assert_eq!(it.len(), 6); - assert_eq!(it.next().unwrap(), 0); - assert_eq!(it.len(), 5); - assert_eq!(it.next().unwrap(), 1); - assert_eq!(it.len(), 4); - assert_eq!(it.next().unwrap(), 2); - assert_eq!(it.len(), 3); - assert_eq!(it.peek().unwrap(), &3); - assert_eq!(it.len(), 3); - assert_eq!(it.peek().unwrap(), &3); - assert_eq!(it.len(), 3); - assert_eq!(it.next().unwrap(), 3); - assert_eq!(it.len(), 2); - assert_eq!(it.next().unwrap(), 4); - assert_eq!(it.len(), 1); - assert_eq!(it.peek().unwrap(), &5); - assert_eq!(it.len(), 1); - assert_eq!(it.next().unwrap(), 5); - assert_eq!(it.len(), 0); - assert!(it.peek().is_none()); - assert_eq!(it.len(), 0); - assert!(it.next().is_none()); - assert_eq!(it.len(), 0); - - let mut it = xs.iter().cloned().peekable(); - assert_eq!(it.len(), 6); - assert_eq!(it.peek().unwrap(), &0); - assert_eq!(it.len(), 6); - assert_eq!(it.next_back().unwrap(), 5); - assert_eq!(it.len(), 5); - assert_eq!(it.next_back().unwrap(), 4); - assert_eq!(it.len(), 4); - assert_eq!(it.next_back().unwrap(), 3); - assert_eq!(it.len(), 3); - assert_eq!(it.peek().unwrap(), &0); - assert_eq!(it.len(), 3); - assert_eq!(it.peek().unwrap(), &0); - assert_eq!(it.len(), 3); - assert_eq!(it.next_back().unwrap(), 2); - assert_eq!(it.len(), 2); - assert_eq!(it.next_back().unwrap(), 1); - assert_eq!(it.len(), 1); - assert_eq!(it.peek().unwrap(), &0); - assert_eq!(it.len(), 1); - assert_eq!(it.next_back().unwrap(), 0); - assert_eq!(it.len(), 0); - assert!(it.peek().is_none()); - assert_eq!(it.len(), 0); - assert!(it.next_back().is_none()); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_iterator_peekable_count() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [10]; - let zs: [i32; 0] = []; - - assert_eq!(xs.iter().peekable().count(), 6); - - let mut it = xs.iter().peekable(); - assert_eq!(it.peek(), Some(&&0)); - assert_eq!(it.count(), 6); - - assert_eq!(ys.iter().peekable().count(), 1); - - let mut it = ys.iter().peekable(); - assert_eq!(it.peek(), Some(&&10)); - assert_eq!(it.count(), 1); - - assert_eq!(zs.iter().peekable().count(), 0); - - let mut it = zs.iter().peekable(); - assert_eq!(it.peek(), None); -} - -#[test] -fn test_iterator_peekable_nth() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().peekable(); - - assert_eq!(it.peek(), Some(&&0)); - assert_eq!(it.nth(0), Some(&0)); - assert_eq!(it.peek(), Some(&&1)); - assert_eq!(it.nth(1), Some(&2)); - assert_eq!(it.peek(), Some(&&3)); - assert_eq!(it.nth(2), Some(&5)); - assert_eq!(it.next(), None); -} - -#[test] -fn test_iterator_peekable_last() { - let xs = [0, 1, 2, 3, 4, 5]; - let ys = [0]; - - let mut it = xs.iter().peekable(); - assert_eq!(it.peek(), Some(&&0)); - assert_eq!(it.last(), Some(&5)); - - let mut it = ys.iter().peekable(); - assert_eq!(it.peek(), Some(&&0)); - assert_eq!(it.last(), Some(&0)); - - let mut it = ys.iter().peekable(); - assert_eq!(it.next(), Some(&0)); - assert_eq!(it.peek(), None); - assert_eq!(it.last(), None); -} - -#[test] -fn test_iterator_peekable_fold() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().peekable(); - assert_eq!(it.peek(), Some(&&0)); - let i = it.fold(0, |i, &x| { - assert_eq!(x, xs[i]); - i + 1 - }); - assert_eq!(i, xs.len()); -} - -#[test] -fn test_iterator_peekable_rfold() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().peekable(); - assert_eq!(it.peek(), Some(&&0)); - let i = it.rfold(0, |i, &x| { - assert_eq!(x, xs[xs.len() - 1 - i]); - i + 1 - }); - assert_eq!(i, xs.len()); -} - -#[test] -fn test_iterator_peekable_next_if_eq() { - // first, try on references - let xs = vec!["Heart", "of", "Gold"]; - let mut it = xs.into_iter().peekable(); - // try before `peek()` - assert_eq!(it.next_if_eq(&"trillian"), None); - assert_eq!(it.next_if_eq(&"Heart"), Some("Heart")); - // try after peek() - assert_eq!(it.peek(), Some(&"of")); - assert_eq!(it.next_if_eq(&"of"), Some("of")); - assert_eq!(it.next_if_eq(&"zaphod"), None); - // make sure `next()` still behaves - assert_eq!(it.next(), Some("Gold")); - - // make sure comparison works for owned values - let xs = vec![String::from("Ludicrous"), "speed".into()]; - let mut it = xs.into_iter().peekable(); - // make sure basic functionality works - assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into())); - assert_eq!(it.next_if_eq("speed"), Some("speed".into())); - assert_eq!(it.next_if_eq(""), None); -} - -#[test] -fn test_iterator_peekable_mut() { - let mut it = vec![1, 2, 3].into_iter().peekable(); - if let Some(p) = it.peek_mut() { - if *p == 1 { - *p = 5; - } - } - assert_eq!(it.collect::>(), vec![5, 2, 3]); -} - -/// This is an iterator that follows the Iterator contract, -/// but it is not fused. After having returned None once, it will start -/// producing elements if .next() is called again. -pub struct CycleIter<'a, T> { - index: usize, - data: &'a [T], -} - -pub fn cycle(data: &[T]) -> CycleIter<'_, T> { - CycleIter { index: 0, data } -} - -impl<'a, T> Iterator for CycleIter<'a, T> { - type Item = &'a T; - fn next(&mut self) -> Option { - let elt = self.data.get(self.index); - self.index += 1; - self.index %= 1 + self.data.len(); - elt - } -} - -#[test] -fn test_iterator_peekable_remember_peek_none_1() { - // Check that the loop using .peek() terminates - let data = [1, 2, 3]; - let mut iter = cycle(&data).peekable(); - - let mut n = 0; - while let Some(_) = iter.next() { - let is_the_last = iter.peek().is_none(); - assert_eq!(is_the_last, n == data.len() - 1); - n += 1; - if n > data.len() { - break; - } - } - assert_eq!(n, data.len()); -} - -#[test] -fn test_iterator_peekable_remember_peek_none_2() { - let data = [0]; - let mut iter = cycle(&data).peekable(); - iter.next(); - assert_eq!(iter.peek(), None); - assert_eq!(iter.last(), None); -} - -#[test] -fn test_iterator_peekable_remember_peek_none_3() { - let data = [0]; - let mut iter = cycle(&data).peekable(); - iter.peek(); - assert_eq!(iter.nth(0), Some(&0)); - - let mut iter = cycle(&data).peekable(); - iter.next(); - assert_eq!(iter.peek(), None); - assert_eq!(iter.nth(0), None); -} - -#[test] -fn test_iterator_take_while() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; - let ys = [0, 1, 2, 3, 5, 13]; - let it = xs.iter().take_while(|&x| *x < 15); - let mut i = 0; - for x in it { - assert_eq!(*x, ys[i]); - i += 1; - } - assert_eq!(i, ys.len()); -} - -#[test] -fn test_iterator_skip_while() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; - let ys = [15, 16, 17, 19]; - let it = xs.iter().skip_while(|&x| *x < 15); - let mut i = 0; - for x in it { - assert_eq!(*x, ys[i]); - i += 1; - } - assert_eq!(i, ys.len()); -} - -#[test] -fn test_iterator_skip_while_fold() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; - let ys = [15, 16, 17, 19]; - let it = xs.iter().skip_while(|&x| *x < 15); - let i = it.fold(0, |i, &x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let mut it = xs.iter().skip_while(|&x| *x < 15); - assert_eq!(it.next(), Some(&ys[0])); // process skips before folding - let i = it.fold(1, |i, &x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); -} - -#[test] -fn test_iterator_skip() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - let ys = [13, 15, 16, 17, 19, 20, 30]; - let mut it = xs.iter().skip(5); - let mut i = 0; - while let Some(&x) = it.next() { - assert_eq!(x, ys[i]); - i += 1; - assert_eq!(it.len(), xs.len() - 5 - i); - } - assert_eq!(i, ys.len()); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_iterator_skip_doubleended() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - let mut it = xs.iter().rev().skip(5); - assert_eq!(it.next(), Some(&15)); - assert_eq!(it.by_ref().rev().next(), Some(&0)); - assert_eq!(it.next(), Some(&13)); - assert_eq!(it.by_ref().rev().next(), Some(&1)); - assert_eq!(it.next(), Some(&5)); - assert_eq!(it.by_ref().rev().next(), Some(&2)); - assert_eq!(it.next(), Some(&3)); - assert_eq!(it.next(), None); - let mut it = xs.iter().rev().skip(5).rev(); - assert_eq!(it.next(), Some(&0)); - assert_eq!(it.rev().next(), Some(&15)); - let mut it_base = xs.iter(); - { - let mut it = it_base.by_ref().skip(5).rev(); - assert_eq!(it.next(), Some(&30)); - assert_eq!(it.next(), Some(&20)); - assert_eq!(it.next(), Some(&19)); - assert_eq!(it.next(), Some(&17)); - assert_eq!(it.next(), Some(&16)); - assert_eq!(it.next(), Some(&15)); - assert_eq!(it.next(), Some(&13)); - assert_eq!(it.next(), None); - } - // make sure the skipped parts have not been consumed - assert_eq!(it_base.next(), Some(&0)); - assert_eq!(it_base.next(), Some(&1)); - assert_eq!(it_base.next(), Some(&2)); - assert_eq!(it_base.next(), Some(&3)); - assert_eq!(it_base.next(), Some(&5)); - assert_eq!(it_base.next(), None); - let it = xs.iter().skip(5).rev(); - assert_eq!(it.last(), Some(&13)); -} - -#[test] -fn test_iterator_skip_nth() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - - let mut it = xs.iter().skip(0); - assert_eq!(it.nth(0), Some(&0)); - assert_eq!(it.nth(1), Some(&2)); - - let mut it = xs.iter().skip(5); - assert_eq!(it.nth(0), Some(&13)); - assert_eq!(it.nth(1), Some(&16)); - - let mut it = xs.iter().skip(12); - assert_eq!(it.nth(0), None); -} - -#[test] -fn test_iterator_skip_count() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - - assert_eq!(xs.iter().skip(0).count(), 12); - assert_eq!(xs.iter().skip(1).count(), 11); - assert_eq!(xs.iter().skip(11).count(), 1); - assert_eq!(xs.iter().skip(12).count(), 0); - assert_eq!(xs.iter().skip(13).count(), 0); -} - -#[test] -fn test_iterator_skip_last() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - - assert_eq!(xs.iter().skip(0).last(), Some(&30)); - assert_eq!(xs.iter().skip(1).last(), Some(&30)); - assert_eq!(xs.iter().skip(11).last(), Some(&30)); - assert_eq!(xs.iter().skip(12).last(), None); - assert_eq!(xs.iter().skip(13).last(), None); - - let mut it = xs.iter().skip(5); - assert_eq!(it.next(), Some(&13)); - assert_eq!(it.last(), Some(&30)); -} - -#[test] -fn test_iterator_skip_fold() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; - let ys = [13, 15, 16, 17, 19, 20, 30]; - - let it = xs.iter().skip(5); - let i = it.fold(0, |i, &x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let mut it = xs.iter().skip(5); - assert_eq!(it.next(), Some(&ys[0])); // process skips before folding - let i = it.fold(1, |i, &x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let it = xs.iter().skip(5); - let i = it.rfold(ys.len(), |i, &x| { - let i = i - 1; - assert_eq!(x, ys[i]); - i - }); - assert_eq!(i, 0); - - let mut it = xs.iter().skip(5); - assert_eq!(it.next(), Some(&ys[0])); // process skips before folding - let i = it.rfold(ys.len(), |i, &x| { - let i = i - 1; - assert_eq!(x, ys[i]); - i - }); - assert_eq!(i, 1); -} - -#[test] -fn test_iterator_take() { - let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; - let ys = [0, 1, 2, 3, 5]; - - let mut it = xs.iter().take(ys.len()); - let mut i = 0; - assert_eq!(it.len(), ys.len()); - while let Some(&x) = it.next() { - assert_eq!(x, ys[i]); - i += 1; - assert_eq!(it.len(), ys.len() - i); - } - assert_eq!(i, ys.len()); - assert_eq!(it.len(), 0); - - let mut it = xs.iter().take(ys.len()); - let mut i = 0; - assert_eq!(it.len(), ys.len()); - while let Some(&x) = it.next_back() { - i += 1; - assert_eq!(x, ys[ys.len() - i]); - assert_eq!(it.len(), ys.len() - i); - } - assert_eq!(i, ys.len()); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_iterator_take_nth() { - let xs = [0, 1, 2, 4, 5]; - let mut it = xs.iter(); - { - let mut take = it.by_ref().take(3); - let mut i = 0; - while let Some(&x) = take.nth(0) { - assert_eq!(x, i); - i += 1; - } - } - assert_eq!(it.nth(1), Some(&5)); - assert_eq!(it.nth(0), None); - - let xs = [0, 1, 2, 3, 4]; - let mut it = xs.iter().take(7); - let mut i = 1; - while let Some(&x) = it.nth(1) { - assert_eq!(x, i); - i += 2; - } -} - -#[test] -fn test_iterator_take_nth_back() { - let xs = [0, 1, 2, 4, 5]; - let mut it = xs.iter(); - { - let mut take = it.by_ref().take(3); - let mut i = 0; - while let Some(&x) = take.nth_back(0) { - i += 1; - assert_eq!(x, 3 - i); - } - } - assert_eq!(it.nth_back(0), None); - - let xs = [0, 1, 2, 3, 4]; - let mut it = xs.iter().take(7); - assert_eq!(it.nth_back(1), Some(&3)); - assert_eq!(it.nth_back(1), Some(&1)); - assert_eq!(it.nth_back(1), None); -} - -#[test] -fn test_iterator_take_short() { - let xs = [0, 1, 2, 3]; - - let mut it = xs.iter().take(5); - let mut i = 0; - assert_eq!(it.len(), xs.len()); - while let Some(&x) = it.next() { - assert_eq!(x, xs[i]); - i += 1; - assert_eq!(it.len(), xs.len() - i); - } - assert_eq!(i, xs.len()); - assert_eq!(it.len(), 0); - - let mut it = xs.iter().take(5); - let mut i = 0; - assert_eq!(it.len(), xs.len()); - while let Some(&x) = it.next_back() { - i += 1; - assert_eq!(x, xs[xs.len() - i]); - assert_eq!(it.len(), xs.len() - i); - } - assert_eq!(i, xs.len()); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_iterator_scan() { - // test the type inference - fn add(old: &mut isize, new: &usize) -> Option { - *old += *new as isize; - Some(*old as f64) - } - let xs = [0, 1, 2, 3, 4]; - let ys = [0f64, 1.0, 3.0, 6.0, 10.0]; - - let it = xs.iter().scan(0, add); - let mut i = 0; - for x in it { - assert_eq!(x, ys[i]); - i += 1; - } - assert_eq!(i, ys.len()); -} - -#[test] -fn test_iterator_flat_map() { - let xs = [0, 3, 6]; - let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let it = xs.iter().flat_map(|&x| (x..).step_by(1).take(3)); - let mut i = 0; - for x in it { - assert_eq!(x, ys[i]); - i += 1; - } - assert_eq!(i, ys.len()); -} - -/// Tests `FlatMap::fold` with items already picked off the front and back, -/// to make sure all parts of the `FlatMap` are folded correctly. -#[test] -fn test_iterator_flat_map_fold() { - let xs = [0, 3, 6]; - let ys = [1, 2, 3, 4, 5, 6, 7]; - let mut it = xs.iter().flat_map(|&x| x..x + 3); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next_back(), Some(8)); - let i = it.fold(0, |i, x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let mut it = xs.iter().flat_map(|&x| x..x + 3); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next_back(), Some(8)); - let i = it.rfold(ys.len(), |i, x| { - assert_eq!(x, ys[i - 1]); - i - 1 - }); - assert_eq!(i, 0); -} - -#[test] -fn test_iterator_flatten() { - let xs = [0, 3, 6]; - let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let it = xs.iter().map(|&x| (x..).step_by(1).take(3)).flatten(); - let mut i = 0; - for x in it { - assert_eq!(x, ys[i]); - i += 1; - } - assert_eq!(i, ys.len()); -} - -/// Tests `Flatten::fold` with items already picked off the front and back, -/// to make sure all parts of the `Flatten` are folded correctly. -#[test] -fn test_iterator_flatten_fold() { - let xs = [0, 3, 6]; - let ys = [1, 2, 3, 4, 5, 6, 7]; - let mut it = xs.iter().map(|&x| x..x + 3).flatten(); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next_back(), Some(8)); - let i = it.fold(0, |i, x| { - assert_eq!(x, ys[i]); - i + 1 - }); - assert_eq!(i, ys.len()); - - let mut it = xs.iter().map(|&x| x..x + 3).flatten(); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next_back(), Some(8)); - let i = it.rfold(ys.len(), |i, x| { - assert_eq!(x, ys[i - 1]); - i - 1 - }); - assert_eq!(i, 0); -} - -#[test] -fn test_inspect() { - let xs = [1, 2, 3, 4]; - let mut n = 0; - - let ys = xs.iter().cloned().inspect(|_| n += 1).collect::>(); - - assert_eq!(n, xs.len()); - assert_eq!(&xs[..], &ys[..]); -} - -#[test] -fn test_inspect_fold() { - let xs = [1, 2, 3, 4]; - let mut n = 0; - { - let it = xs.iter().inspect(|_| n += 1); - let i = it.fold(0, |i, &x| { - assert_eq!(x, xs[i]); - i + 1 - }); - assert_eq!(i, xs.len()); - } - assert_eq!(n, xs.len()); - - let mut n = 0; - { - let it = xs.iter().inspect(|_| n += 1); - let i = it.rfold(xs.len(), |i, &x| { - assert_eq!(x, xs[i - 1]); - i - 1 - }); - assert_eq!(i, 0); - } - assert_eq!(n, xs.len()); -} - -#[test] -fn test_cycle() { - let cycle_len = 3; - let it = (0..).step_by(1).take(cycle_len).cycle(); - assert_eq!(it.size_hint(), (usize::MAX, None)); - for (i, x) in it.take(100).enumerate() { - assert_eq!(i % cycle_len, x); - } - - let mut it = (0..).step_by(1).take(0).cycle(); - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next(), None); - - assert_eq!(empty::().cycle().fold(0, |acc, x| acc + x), 0); - - assert_eq!(once(1).cycle().skip(1).take(4).fold(0, |acc, x| acc + x), 4); - - assert_eq!((0..10).cycle().take(5).sum::(), 10); - assert_eq!((0..10).cycle().take(15).sum::(), 55); - assert_eq!((0..10).cycle().take(25).sum::(), 100); - - let mut iter = (0..10).cycle(); - iter.nth(14); - assert_eq!(iter.take(8).sum::(), 38); - - let mut iter = (0..10).cycle(); - iter.nth(9); - assert_eq!(iter.take(3).sum::(), 3); -} - -#[test] -fn test_iterator_nth() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().nth(i).unwrap(), &v[i]); - } - assert_eq!(v.iter().nth(v.len()), None); -} - -#[test] -fn test_iterator_nth_back() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().nth_back(i).unwrap(), &v[v.len() - 1 - i]); - } - assert_eq!(v.iter().nth_back(v.len()), None); -} - -#[test] -fn test_iterator_rev_nth_back() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().rev().nth_back(i).unwrap(), &v[i]); - } - assert_eq!(v.iter().rev().nth_back(v.len()), None); -} - -#[test] -fn test_iterator_rev_nth() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().rev().nth(i).unwrap(), &v[v.len() - 1 - i]); - } - assert_eq!(v.iter().rev().nth(v.len()), None); -} - -#[test] -fn test_iterator_advance_by() { - let v: &[_] = &[0, 1, 2, 3, 4]; - - for i in 0..v.len() { - let mut iter = v.iter(); - assert_eq!(iter.advance_by(i), Ok(())); - assert_eq!(iter.next().unwrap(), &v[i]); - assert_eq!(iter.advance_by(100), Err(v.len() - 1 - i)); - } - - assert_eq!(v.iter().advance_by(v.len()), Ok(())); - assert_eq!(v.iter().advance_by(100), Err(v.len())); -} - -#[test] -fn test_iterator_advance_back_by() { - let v: &[_] = &[0, 1, 2, 3, 4]; - - for i in 0..v.len() { - let mut iter = v.iter(); - assert_eq!(iter.advance_back_by(i), Ok(())); - assert_eq!(iter.next_back().unwrap(), &v[v.len() - 1 - i]); - assert_eq!(iter.advance_back_by(100), Err(v.len() - 1 - i)); - } - - assert_eq!(v.iter().advance_back_by(v.len()), Ok(())); - assert_eq!(v.iter().advance_back_by(100), Err(v.len())); -} - -#[test] -fn test_iterator_rev_advance_by() { - let v: &[_] = &[0, 1, 2, 3, 4]; - - for i in 0..v.len() { - let mut iter = v.iter().rev(); - assert_eq!(iter.advance_by(i), Ok(())); - assert_eq!(iter.next().unwrap(), &v[v.len() - 1 - i]); - assert_eq!(iter.advance_by(100), Err(v.len() - 1 - i)); - } - - assert_eq!(v.iter().rev().advance_by(v.len()), Ok(())); - assert_eq!(v.iter().rev().advance_by(100), Err(v.len())); -} - -#[test] -fn test_iterator_rev_advance_back_by() { - let v: &[_] = &[0, 1, 2, 3, 4]; - - for i in 0..v.len() { - let mut iter = v.iter().rev(); - assert_eq!(iter.advance_back_by(i), Ok(())); - assert_eq!(iter.next_back().unwrap(), &v[i]); - assert_eq!(iter.advance_back_by(100), Err(v.len() - 1 - i)); - } - - assert_eq!(v.iter().rev().advance_back_by(v.len()), Ok(())); - assert_eq!(v.iter().rev().advance_back_by(100), Err(v.len())); -} - -#[test] -fn test_iterator_last() { - let v: &[_] = &[0, 1, 2, 3, 4]; - assert_eq!(v.iter().last().unwrap(), &4); - assert_eq!(v[..1].iter().last().unwrap(), &0); -} - -#[test] -fn test_iterator_len() { - let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(v[..4].iter().count(), 4); - assert_eq!(v[..10].iter().count(), 10); - assert_eq!(v[..0].iter().count(), 0); -} - -#[test] -fn test_iterator_sum() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(v[..4].iter().cloned().sum::(), 6); - assert_eq!(v.iter().cloned().sum::(), 55); - assert_eq!(v[..0].iter().cloned().sum::(), 0); -} - -#[test] -fn test_iterator_sum_result() { - let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().sum::>(), Ok(10)); - let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().sum::>(), Err(())); - - #[derive(PartialEq, Debug)] - struct S(Result); - - impl Sum> for S { - fn sum>>(mut iter: I) -> Self { - // takes the sum by repeatedly calling `next` on `iter`, - // thus testing that repeated calls to `ResultShunt::try_fold` - // produce the expected results - Self(iter.by_ref().sum()) - } - } - - let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().sum::(), S(Ok(10))); - let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().sum::(), S(Err(()))); -} - -#[test] -fn test_iterator_sum_option() { - let v: &[Option] = &[Some(1), Some(2), Some(3), Some(4)]; - assert_eq!(v.iter().cloned().sum::>(), Some(10)); - let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; - assert_eq!(v.iter().cloned().sum::>(), None); -} - -#[test] -fn test_iterator_product() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(v[..4].iter().cloned().product::(), 0); - assert_eq!(v[1..5].iter().cloned().product::(), 24); - assert_eq!(v[..0].iter().cloned().product::(), 1); -} - -#[test] -fn test_iterator_product_result() { - let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().product::>(), Ok(24)); - let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; - assert_eq!(v.iter().cloned().product::>(), Err(())); -} - -/// A wrapper struct that implements `Eq` and `Ord` based on the wrapped -/// integer modulo 3. Used to test that `Iterator::max` and `Iterator::min` -/// return the correct element if some of them are equal. -#[derive(Debug)] -struct Mod3(i32); - -impl PartialEq for Mod3 { - fn eq(&self, other: &Self) -> bool { - self.0 % 3 == other.0 % 3 - } -} - -impl Eq for Mod3 {} - -impl PartialOrd for Mod3 { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Mod3 { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - (self.0 % 3).cmp(&(other.0 % 3)) - } -} - -#[test] -fn test_iterator_product_option() { - let v: &[Option] = &[Some(1), Some(2), Some(3), Some(4)]; - assert_eq!(v.iter().cloned().product::>(), Some(24)); - let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; - assert_eq!(v.iter().cloned().product::>(), None); -} - -#[test] -fn test_iterator_max() { - let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(v[..4].iter().cloned().max(), Some(3)); - assert_eq!(v.iter().cloned().max(), Some(10)); - assert_eq!(v[..0].iter().cloned().max(), None); - assert_eq!(v.iter().cloned().map(Mod3).max().map(|x| x.0), Some(8)); -} - -#[test] -fn test_iterator_min() { - let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!(v[..4].iter().cloned().min(), Some(0)); - assert_eq!(v.iter().cloned().min(), Some(0)); - assert_eq!(v[..0].iter().cloned().min(), None); - assert_eq!(v.iter().cloned().map(Mod3).min().map(|x| x.0), Some(0)); -} - -#[test] -fn test_iterator_size_hint() { - let c = (0..).step_by(1); - let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - let v2 = &[10, 11, 12]; - let vi = v.iter(); - - assert_eq!((0..).size_hint(), (usize::MAX, None)); - assert_eq!(c.size_hint(), (usize::MAX, None)); - assert_eq!(vi.clone().size_hint(), (10, Some(10))); - - assert_eq!(c.clone().take(5).size_hint(), (5, Some(5))); - assert_eq!(c.clone().skip(5).size_hint().1, None); - assert_eq!(c.clone().take_while(|_| false).size_hint(), (0, None)); - assert_eq!(c.clone().map_while(|_| None::<()>).size_hint(), (0, None)); - assert_eq!(c.clone().skip_while(|_| false).size_hint(), (0, None)); - assert_eq!(c.clone().enumerate().size_hint(), (usize::MAX, None)); - assert_eq!(c.clone().chain(vi.clone().cloned()).size_hint(), (usize::MAX, None)); - assert_eq!(c.clone().zip(vi.clone()).size_hint(), (10, Some(10))); - assert_eq!(c.clone().scan(0, |_, _| Some(0)).size_hint(), (0, None)); - assert_eq!(c.clone().filter(|_| false).size_hint(), (0, None)); - assert_eq!(c.clone().map(|_| 0).size_hint(), (usize::MAX, None)); - assert_eq!(c.filter_map(|_| Some(0)).size_hint(), (0, None)); - - assert_eq!(vi.clone().take(5).size_hint(), (5, Some(5))); - assert_eq!(vi.clone().take(12).size_hint(), (10, Some(10))); - assert_eq!(vi.clone().skip(3).size_hint(), (7, Some(7))); - assert_eq!(vi.clone().skip(12).size_hint(), (0, Some(0))); - assert_eq!(vi.clone().take_while(|_| false).size_hint(), (0, Some(10))); - assert_eq!(vi.clone().map_while(|_| None::<()>).size_hint(), (0, Some(10))); - assert_eq!(vi.clone().skip_while(|_| false).size_hint(), (0, Some(10))); - assert_eq!(vi.clone().enumerate().size_hint(), (10, Some(10))); - assert_eq!(vi.clone().chain(v2).size_hint(), (13, Some(13))); - assert_eq!(vi.clone().zip(v2).size_hint(), (3, Some(3))); - assert_eq!(vi.clone().scan(0, |_, _| Some(0)).size_hint(), (0, Some(10))); - assert_eq!(vi.clone().filter(|_| false).size_hint(), (0, Some(10))); - assert_eq!(vi.clone().map(|&i| i + 1).size_hint(), (10, Some(10))); - assert_eq!(vi.filter_map(|_| Some(0)).size_hint(), (0, Some(10))); -} - -#[test] -fn test_collect() { - let a = vec![1, 2, 3, 4, 5]; - let b: Vec = a.iter().cloned().collect(); - assert!(a == b); -} - -#[test] -fn test_all() { - let v: Box<[isize]> = Box::new([1, 2, 3, 4, 5]); - assert!(v.iter().all(|&x| x < 10)); - assert!(!v.iter().all(|&x| x % 2 == 0)); - assert!(!v.iter().all(|&x| x > 100)); - assert!(v[..0].iter().all(|_| panic!())); -} - -#[test] -fn test_any() { - let v: Box<[isize]> = Box::new([1, 2, 3, 4, 5]); - assert!(v.iter().any(|&x| x < 10)); - assert!(v.iter().any(|&x| x % 2 == 0)); - assert!(!v.iter().any(|&x| x > 100)); - assert!(!v[..0].iter().any(|_| panic!())); -} - -#[test] -fn test_find() { - let v: &[isize] = &[1, 3, 9, 27, 103, 14, 11]; - assert_eq!(*v.iter().find(|&&x| x & 1 == 0).unwrap(), 14); - assert_eq!(*v.iter().find(|&&x| x % 3 == 0).unwrap(), 3); - assert!(v.iter().find(|&&x| x % 12 == 0).is_none()); -} - -#[test] -fn test_find_map() { - let xs: &[isize] = &[]; - assert_eq!(xs.iter().find_map(half_if_even), None); - let xs: &[isize] = &[3, 5]; - assert_eq!(xs.iter().find_map(half_if_even), None); - let xs: &[isize] = &[4, 5]; - assert_eq!(xs.iter().find_map(half_if_even), Some(2)); - let xs: &[isize] = &[3, 6]; - assert_eq!(xs.iter().find_map(half_if_even), Some(3)); - - let xs: &[isize] = &[1, 2, 3, 4, 5, 6, 7]; - let mut iter = xs.iter(); - assert_eq!(iter.find_map(half_if_even), Some(1)); - assert_eq!(iter.find_map(half_if_even), Some(2)); - assert_eq!(iter.find_map(half_if_even), Some(3)); - assert_eq!(iter.next(), Some(&7)); - - fn half_if_even(x: &isize) -> Option { - if x % 2 == 0 { Some(x / 2) } else { None } - } -} - -#[test] -fn test_try_find() { - let xs: &[isize] = &[]; - assert_eq!(xs.iter().try_find(testfn), Ok(None)); - let xs: &[isize] = &[1, 2, 3, 4]; - assert_eq!(xs.iter().try_find(testfn), Ok(Some(&2))); - let xs: &[isize] = &[1, 3, 4]; - assert_eq!(xs.iter().try_find(testfn), Err(())); - - let xs: &[isize] = &[1, 2, 3, 4, 5, 6, 7]; - let mut iter = xs.iter(); - assert_eq!(iter.try_find(testfn), Ok(Some(&2))); - assert_eq!(iter.try_find(testfn), Err(())); - assert_eq!(iter.next(), Some(&5)); - - fn testfn(x: &&isize) -> Result { - if **x == 2 { - return Ok(true); - } - if **x == 4 { - return Err(()); - } - Ok(false) - } -} - -#[test] -fn test_try_find_api_usability() -> Result<(), Box> { - let a = ["1", "2"]; - - let is_my_num = |s: &str, search: i32| -> Result { - Ok(s.parse::()? == search) - }; - - let val = a.iter().try_find(|&&s| is_my_num(s, 2))?; - assert_eq!(val, Some(&"2")); - - Ok(()) -} - -#[test] -fn test_position() { - let v = &[1, 3, 9, 27, 103, 14, 11]; - assert_eq!(v.iter().position(|x| *x & 1 == 0).unwrap(), 5); - assert_eq!(v.iter().position(|x| *x % 3 == 0).unwrap(), 1); - assert!(v.iter().position(|x| *x % 12 == 0).is_none()); -} - -#[test] -fn test_count() { - let xs = &[1, 2, 2, 1, 5, 9, 0, 2]; - assert_eq!(xs.iter().filter(|x| **x == 2).count(), 3); - assert_eq!(xs.iter().filter(|x| **x == 5).count(), 1); - assert_eq!(xs.iter().filter(|x| **x == 95).count(), 0); -} - -#[test] -fn test_max_by_key() { - let xs: &[isize] = &[-3, 0, 1, 5, -10]; - assert_eq!(*xs.iter().max_by_key(|x| x.abs()).unwrap(), -10); -} - -#[test] -fn test_max_by() { - let xs: &[isize] = &[-3, 0, 1, 5, -10]; - assert_eq!(*xs.iter().max_by(|x, y| x.abs().cmp(&y.abs())).unwrap(), -10); -} - -#[test] -fn test_min_by_key() { - let xs: &[isize] = &[-3, 0, 1, 5, -10]; - assert_eq!(*xs.iter().min_by_key(|x| x.abs()).unwrap(), 0); -} - -#[test] -fn test_min_by() { - let xs: &[isize] = &[-3, 0, 1, 5, -10]; - assert_eq!(*xs.iter().min_by(|x, y| x.abs().cmp(&y.abs())).unwrap(), 0); -} - -#[test] -fn test_by_ref() { - let mut xs = 0..10; - // sum the first five values - let partial_sum = xs.by_ref().take(5).fold(0, |a, b| a + b); - assert_eq!(partial_sum, 10); - assert_eq!(xs.next(), Some(5)); -} - -#[test] -fn test_rev() { - let xs = [2, 4, 6, 8, 10, 12, 14, 16]; - let mut it = xs.iter(); - it.next(); - it.next(); - assert!(it.rev().cloned().collect::>() == vec![16, 14, 12, 10, 8, 6]); -} - -#[test] -fn test_copied() { - let xs = [2, 4, 6, 8]; - - let mut it = xs.iter().copied(); - assert_eq!(it.len(), 4); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.len(), 3); - assert_eq!(it.next(), Some(4)); - assert_eq!(it.len(), 2); - assert_eq!(it.next_back(), Some(8)); - assert_eq!(it.len(), 1); - assert_eq!(it.next_back(), Some(6)); - assert_eq!(it.len(), 0); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_cloned() { - let xs = [2, 4, 6, 8]; - - let mut it = xs.iter().cloned(); - assert_eq!(it.len(), 4); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.len(), 3); - assert_eq!(it.next(), Some(4)); - assert_eq!(it.len(), 2); - assert_eq!(it.next_back(), Some(8)); - assert_eq!(it.len(), 1); - assert_eq!(it.next_back(), Some(6)); - assert_eq!(it.len(), 0); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_cloned_side_effects() { - let mut count = 0; - { - let iter = [1, 2, 3] - .iter() - .map(|x| { - count += 1; - x - }) - .cloned() - .zip(&[1]); - for _ in iter {} - } - assert_eq!(count, 2); -} - -#[test] -fn test_double_ended_map() { - let xs = [1, 2, 3, 4, 5, 6]; - let mut it = xs.iter().map(|&x| x * -1); - assert_eq!(it.next(), Some(-1)); - assert_eq!(it.next(), Some(-2)); - assert_eq!(it.next_back(), Some(-6)); - assert_eq!(it.next_back(), Some(-5)); - assert_eq!(it.next(), Some(-3)); - assert_eq!(it.next_back(), Some(-4)); - assert_eq!(it.next(), None); -} - -#[test] -fn test_double_ended_enumerate() { - let xs = [1, 2, 3, 4, 5, 6]; - let mut it = xs.iter().cloned().enumerate(); - assert_eq!(it.next(), Some((0, 1))); - assert_eq!(it.next(), Some((1, 2))); - assert_eq!(it.next_back(), Some((5, 6))); - assert_eq!(it.next_back(), Some((4, 5))); - assert_eq!(it.next_back(), Some((3, 4))); - assert_eq!(it.next_back(), Some((2, 3))); - assert_eq!(it.next(), None); -} - -#[test] -fn test_double_ended_zip() { - let xs = [1, 2, 3, 4, 5, 6]; - let ys = [1, 2, 3, 7]; - let a = xs.iter().cloned(); - let b = ys.iter().cloned(); - let mut it = a.zip(b); - assert_eq!(it.next(), Some((1, 1))); - assert_eq!(it.next(), Some((2, 2))); - assert_eq!(it.next_back(), Some((4, 7))); - assert_eq!(it.next_back(), Some((3, 3))); - assert_eq!(it.next(), None); -} - -#[test] -fn test_double_ended_filter() { - let xs = [1, 2, 3, 4, 5, 6]; - let mut it = xs.iter().filter(|&x| *x & 1 == 0); - assert_eq!(it.next_back().unwrap(), &6); - assert_eq!(it.next_back().unwrap(), &4); - assert_eq!(it.next().unwrap(), &2); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_double_ended_filter_map() { - let xs = [1, 2, 3, 4, 5, 6]; - let mut it = xs.iter().filter_map(|&x| if x & 1 == 0 { Some(x * 2) } else { None }); - assert_eq!(it.next_back().unwrap(), 12); - assert_eq!(it.next_back().unwrap(), 8); - assert_eq!(it.next().unwrap(), 4); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_double_ended_chain() { - let xs = [1, 2, 3, 4, 5]; - let ys = [7, 9, 11]; - let mut it = xs.iter().chain(&ys).rev(); - assert_eq!(it.next().unwrap(), &11); - assert_eq!(it.next().unwrap(), &9); - assert_eq!(it.next_back().unwrap(), &1); - assert_eq!(it.next_back().unwrap(), &2); - assert_eq!(it.next_back().unwrap(), &3); - assert_eq!(it.next_back().unwrap(), &4); - assert_eq!(it.next_back().unwrap(), &5); - assert_eq!(it.next_back().unwrap(), &7); - assert_eq!(it.next_back(), None); - - // test that .chain() is well behaved with an unfused iterator - struct CrazyIterator(bool); - impl CrazyIterator { - fn new() -> CrazyIterator { - CrazyIterator(false) - } - } - impl Iterator for CrazyIterator { - type Item = i32; - fn next(&mut self) -> Option { - if self.0 { - Some(99) - } else { - self.0 = true; - None - } - } - } - - impl DoubleEndedIterator for CrazyIterator { - fn next_back(&mut self) -> Option { - self.next() - } - } - - assert_eq!(CrazyIterator::new().chain(0..10).rev().last(), Some(0)); - assert!((0..10).chain(CrazyIterator::new()).rev().any(|i| i == 0)); -} - -#[test] -fn test_rposition() { - fn f(xy: &(isize, char)) -> bool { - let (_x, y) = *xy; - y == 'b' - } - fn g(xy: &(isize, char)) -> bool { - let (_x, y) = *xy; - y == 'd' - } - let v = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; - - assert_eq!(v.iter().rposition(f), Some(3)); - assert!(v.iter().rposition(g).is_none()); -} - -#[test] -fn test_rev_rposition() { - let v = [0, 0, 1, 1]; - assert_eq!(v.iter().rev().rposition(|&x| x == 1), Some(1)); -} - -#[test] -#[should_panic] -fn test_rposition_panic() { - let v: [(Box<_>, Box<_>); 4] = [(box 0, box 0), (box 0, box 0), (box 0, box 0), (box 0, box 0)]; - let mut i = 0; - v.iter().rposition(|_elt| { - if i == 2 { - panic!() - } - i += 1; - false - }); -} - -#[test] -fn test_double_ended_flat_map() { - let u = [0, 1]; - let v = [5, 6, 7, 8]; - let mut it = u.iter().flat_map(|x| &v[*x..v.len()]); - assert_eq!(it.next_back().unwrap(), &8); - assert_eq!(it.next().unwrap(), &5); - assert_eq!(it.next_back().unwrap(), &7); - assert_eq!(it.next_back().unwrap(), &6); - assert_eq!(it.next_back().unwrap(), &8); - assert_eq!(it.next().unwrap(), &6); - assert_eq!(it.next_back().unwrap(), &7); - assert_eq!(it.next_back(), None); - assert_eq!(it.next(), None); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_double_ended_flatten() { - let u = [0, 1]; - let v = [5, 6, 7, 8]; - let mut it = u.iter().map(|x| &v[*x..v.len()]).flatten(); - assert_eq!(it.next_back().unwrap(), &8); - assert_eq!(it.next().unwrap(), &5); - assert_eq!(it.next_back().unwrap(), &7); - assert_eq!(it.next_back().unwrap(), &6); - assert_eq!(it.next_back().unwrap(), &8); - assert_eq!(it.next().unwrap(), &6); - assert_eq!(it.next_back().unwrap(), &7); - assert_eq!(it.next_back(), None); - assert_eq!(it.next(), None); - assert_eq!(it.next_back(), None); -} - -#[test] -fn test_double_ended_range() { - assert_eq!((11..14).rev().collect::>(), [13, 12, 11]); - for _ in (10..0).rev() { - panic!("unreachable"); - } - - assert_eq!((11..14).rev().collect::>(), [13, 12, 11]); - for _ in (10..0).rev() { - panic!("unreachable"); - } -} - -#[test] -fn test_range() { - assert_eq!((0..5).collect::>(), [0, 1, 2, 3, 4]); - assert_eq!((-10..-1).collect::>(), [-10, -9, -8, -7, -6, -5, -4, -3, -2]); - assert_eq!((0..5).rev().collect::>(), [4, 3, 2, 1, 0]); - assert_eq!((200..-5).count(), 0); - assert_eq!((200..-5).rev().count(), 0); - assert_eq!((200..200).count(), 0); - assert_eq!((200..200).rev().count(), 0); - - assert_eq!((0..100).size_hint(), (100, Some(100))); - // this test is only meaningful when sizeof usize < sizeof u64 - assert_eq!((usize::MAX - 1..usize::MAX).size_hint(), (1, Some(1))); - assert_eq!((-10..-1).size_hint(), (9, Some(9))); - assert_eq!((-1..-10).size_hint(), (0, Some(0))); - - assert_eq!((-70..58).size_hint(), (128, Some(128))); - assert_eq!((-128..127).size_hint(), (255, Some(255))); - assert_eq!( - (-2..isize::MAX).size_hint(), - (isize::MAX as usize + 2, Some(isize::MAX as usize + 2)) - ); -} - -#[test] -fn test_char_range() { - use std::char; - // Miri is too slow - let from = if cfg!(miri) { char::from_u32(0xD800 - 10).unwrap() } else { '\0' }; - let to = if cfg!(miri) { char::from_u32(0xDFFF + 10).unwrap() } else { char::MAX }; - assert!((from..=to).eq((from as u32..=to as u32).filter_map(char::from_u32))); - assert!((from..=to).rev().eq((from as u32..=to as u32).filter_map(char::from_u32).rev())); - - assert_eq!(('\u{D7FF}'..='\u{E000}').count(), 2); - assert_eq!(('\u{D7FF}'..='\u{E000}').size_hint(), (2, Some(2))); - assert_eq!(('\u{D7FF}'..'\u{E000}').count(), 1); - assert_eq!(('\u{D7FF}'..'\u{E000}').size_hint(), (1, Some(1))); -} - -#[test] -fn test_range_exhaustion() { - let mut r = 10..10; - assert!(r.is_empty()); - assert_eq!(r.next(), None); - assert_eq!(r.next_back(), None); - assert_eq!(r, 10..10); - - let mut r = 10..12; - assert_eq!(r.next(), Some(10)); - assert_eq!(r.next(), Some(11)); - assert!(r.is_empty()); - assert_eq!(r, 12..12); - assert_eq!(r.next(), None); - - let mut r = 10..12; - assert_eq!(r.next_back(), Some(11)); - assert_eq!(r.next_back(), Some(10)); - assert!(r.is_empty()); - assert_eq!(r, 10..10); - assert_eq!(r.next_back(), None); - - let mut r = 100..10; - assert!(r.is_empty()); - assert_eq!(r.next(), None); - assert_eq!(r.next_back(), None); - assert_eq!(r, 100..10); -} - -#[test] -fn test_range_inclusive_exhaustion() { - let mut r = 10..=10; - assert_eq!(r.next(), Some(10)); - assert!(r.is_empty()); - assert_eq!(r.next(), None); - assert_eq!(r.next(), None); - - assert_eq!(*r.start(), 10); - assert_eq!(*r.end(), 10); - assert_ne!(r, 10..=10); - - let mut r = 10..=10; - assert_eq!(r.next_back(), Some(10)); - assert!(r.is_empty()); - assert_eq!(r.next_back(), None); - - assert_eq!(*r.start(), 10); - assert_eq!(*r.end(), 10); - assert_ne!(r, 10..=10); - - let mut r = 10..=12; - assert_eq!(r.next(), Some(10)); - assert_eq!(r.next(), Some(11)); - assert_eq!(r.next(), Some(12)); - assert!(r.is_empty()); - assert_eq!(r.next(), None); - - let mut r = 10..=12; - assert_eq!(r.next_back(), Some(12)); - assert_eq!(r.next_back(), Some(11)); - assert_eq!(r.next_back(), Some(10)); - assert!(r.is_empty()); - assert_eq!(r.next_back(), None); - - let mut r = 10..=12; - assert_eq!(r.nth(2), Some(12)); - assert!(r.is_empty()); - assert_eq!(r.next(), None); - - let mut r = 10..=12; - assert_eq!(r.nth(5), None); - assert!(r.is_empty()); - assert_eq!(r.next(), None); - - let mut r = 100..=10; - assert_eq!(r.next(), None); - assert!(r.is_empty()); - assert_eq!(r.next(), None); - assert_eq!(r.next(), None); - assert_eq!(r, 100..=10); - - let mut r = 100..=10; - assert_eq!(r.next_back(), None); - assert!(r.is_empty()); - assert_eq!(r.next_back(), None); - assert_eq!(r.next_back(), None); - assert_eq!(r, 100..=10); -} - -#[test] -fn test_range_nth() { - assert_eq!((10..15).nth(0), Some(10)); - assert_eq!((10..15).nth(1), Some(11)); - assert_eq!((10..15).nth(4), Some(14)); - assert_eq!((10..15).nth(5), None); - - let mut r = 10..20; - assert_eq!(r.nth(2), Some(12)); - assert_eq!(r, 13..20); - assert_eq!(r.nth(2), Some(15)); - assert_eq!(r, 16..20); - assert_eq!(r.nth(10), None); - assert_eq!(r, 20..20); -} - -#[test] -fn test_range_nth_back() { - assert_eq!((10..15).nth_back(0), Some(14)); - assert_eq!((10..15).nth_back(1), Some(13)); - assert_eq!((10..15).nth_back(4), Some(10)); - assert_eq!((10..15).nth_back(5), None); - assert_eq!((-120..80_i8).nth_back(199), Some(-120)); - - let mut r = 10..20; - assert_eq!(r.nth_back(2), Some(17)); - assert_eq!(r, 10..17); - assert_eq!(r.nth_back(2), Some(14)); - assert_eq!(r, 10..14); - assert_eq!(r.nth_back(10), None); - assert_eq!(r, 10..10); -} - -#[test] -fn test_range_from_nth() { - assert_eq!((10..).nth(0), Some(10)); - assert_eq!((10..).nth(1), Some(11)); - assert_eq!((10..).nth(4), Some(14)); - - let mut r = 10..; - assert_eq!(r.nth(2), Some(12)); - assert_eq!(r, 13..); - assert_eq!(r.nth(2), Some(15)); - assert_eq!(r, 16..); - assert_eq!(r.nth(10), Some(26)); - assert_eq!(r, 27..); - - assert_eq!((0..).size_hint(), (usize::MAX, None)); -} - -fn is_trusted_len(_: I) {} - -#[test] -fn test_range_from_take() { - let mut it = (0..).take(3); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.next(), Some(1)); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.next(), None); - is_trusted_len((0..).take(3)); - assert_eq!((0..).take(3).size_hint(), (3, Some(3))); - assert_eq!((0..).take(0).size_hint(), (0, Some(0))); - assert_eq!((0..).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); -} - -#[test] -fn test_range_from_take_collect() { - let v: Vec<_> = (0..).take(3).collect(); - assert_eq!(v, vec![0, 1, 2]); -} - -#[test] -fn test_range_inclusive_nth() { - assert_eq!((10..=15).nth(0), Some(10)); - assert_eq!((10..=15).nth(1), Some(11)); - assert_eq!((10..=15).nth(5), Some(15)); - assert_eq!((10..=15).nth(6), None); - - let mut exhausted_via_next = 10_u8..=20; - while exhausted_via_next.next().is_some() {} - - let mut r = 10_u8..=20; - assert_eq!(r.nth(2), Some(12)); - assert_eq!(r, 13..=20); - assert_eq!(r.nth(2), Some(15)); - assert_eq!(r, 16..=20); - assert_eq!(r.is_empty(), false); - assert_eq!(ExactSizeIterator::is_empty(&r), false); - assert_eq!(r.nth(10), None); - assert_eq!(r.is_empty(), true); - assert_eq!(r, exhausted_via_next); - assert_eq!(ExactSizeIterator::is_empty(&r), true); -} - -#[test] -fn test_range_inclusive_nth_back() { - assert_eq!((10..=15).nth_back(0), Some(15)); - assert_eq!((10..=15).nth_back(1), Some(14)); - assert_eq!((10..=15).nth_back(5), Some(10)); - assert_eq!((10..=15).nth_back(6), None); - assert_eq!((-120..=80_i8).nth_back(200), Some(-120)); - - let mut exhausted_via_next_back = 10_u8..=20; - while exhausted_via_next_back.next_back().is_some() {} - - let mut r = 10_u8..=20; - assert_eq!(r.nth_back(2), Some(18)); - assert_eq!(r, 10..=17); - assert_eq!(r.nth_back(2), Some(15)); - assert_eq!(r, 10..=14); - assert_eq!(r.is_empty(), false); - assert_eq!(ExactSizeIterator::is_empty(&r), false); - assert_eq!(r.nth_back(10), None); - assert_eq!(r.is_empty(), true); - assert_eq!(r, exhausted_via_next_back); - assert_eq!(ExactSizeIterator::is_empty(&r), true); -} - -#[test] -fn test_range_len() { - assert_eq!((0..10_u8).len(), 10); - assert_eq!((9..10_u8).len(), 1); - assert_eq!((10..10_u8).len(), 0); - assert_eq!((11..10_u8).len(), 0); - assert_eq!((100..10_u8).len(), 0); -} - -#[test] -fn test_range_inclusive_len() { - assert_eq!((0..=10_u8).len(), 11); - assert_eq!((9..=10_u8).len(), 2); - assert_eq!((10..=10_u8).len(), 1); - assert_eq!((11..=10_u8).len(), 0); - assert_eq!((100..=10_u8).len(), 0); -} - -#[test] -fn test_range_step() { - #![allow(deprecated)] - - assert_eq!((0..20).step_by(5).collect::>(), [0, 5, 10, 15]); - assert_eq!((1..21).rev().step_by(5).collect::>(), [20, 15, 10, 5]); - assert_eq!((1..21).rev().step_by(6).collect::>(), [20, 14, 8, 2]); - assert_eq!((200..255).step_by(50).collect::>(), [200, 250]); - assert_eq!((200..-5).step_by(1).collect::>(), []); - assert_eq!((200..200).step_by(1).collect::>(), []); - - assert_eq!((0..20).step_by(1).size_hint(), (20, Some(20))); - assert_eq!((0..20).step_by(21).size_hint(), (1, Some(1))); - assert_eq!((0..20).step_by(5).size_hint(), (4, Some(4))); - assert_eq!((1..21).rev().step_by(5).size_hint(), (4, Some(4))); - assert_eq!((1..21).rev().step_by(6).size_hint(), (4, Some(4))); - assert_eq!((20..-5).step_by(1).size_hint(), (0, Some(0))); - assert_eq!((20..20).step_by(1).size_hint(), (0, Some(0))); - assert_eq!((i8::MIN..i8::MAX).step_by(-(i8::MIN as i32) as usize).size_hint(), (2, Some(2))); - assert_eq!((i16::MIN..i16::MAX).step_by(i16::MAX as usize).size_hint(), (3, Some(3))); - assert_eq!((isize::MIN..isize::MAX).step_by(1).size_hint(), (usize::MAX, Some(usize::MAX))); -} - -#[test] -fn test_step_by_skip() { - assert_eq!((0..640).step_by(128).skip(1).collect::>(), [128, 256, 384, 512]); - assert_eq!((0..=50).step_by(10).nth(3), Some(30)); - assert_eq!((200..=255u8).step_by(10).nth(3), Some(230)); -} - -#[test] -fn test_range_inclusive_step() { - assert_eq!((0..=50).step_by(10).collect::>(), [0, 10, 20, 30, 40, 50]); - assert_eq!((0..=5).step_by(1).collect::>(), [0, 1, 2, 3, 4, 5]); - assert_eq!((200..=255u8).step_by(10).collect::>(), [200, 210, 220, 230, 240, 250]); - assert_eq!((250..=255u8).step_by(1).collect::>(), [250, 251, 252, 253, 254, 255]); -} - -#[test] -fn test_range_last_max() { - assert_eq!((0..20).last(), Some(19)); - assert_eq!((-20..0).last(), Some(-1)); - assert_eq!((5..5).last(), None); - - assert_eq!((0..20).max(), Some(19)); - assert_eq!((-20..0).max(), Some(-1)); - assert_eq!((5..5).max(), None); -} - -#[test] -fn test_range_inclusive_last_max() { - assert_eq!((0..=20).last(), Some(20)); - assert_eq!((-20..=0).last(), Some(0)); - assert_eq!((5..=5).last(), Some(5)); - let mut r = 10..=10; - r.next(); - assert_eq!(r.last(), None); - - assert_eq!((0..=20).max(), Some(20)); - assert_eq!((-20..=0).max(), Some(0)); - assert_eq!((5..=5).max(), Some(5)); - let mut r = 10..=10; - r.next(); - assert_eq!(r.max(), None); -} - -#[test] -fn test_range_min() { - assert_eq!((0..20).min(), Some(0)); - assert_eq!((-20..0).min(), Some(-20)); - assert_eq!((5..5).min(), None); -} - -#[test] -fn test_range_inclusive_min() { - assert_eq!((0..=20).min(), Some(0)); - assert_eq!((-20..=0).min(), Some(-20)); - assert_eq!((5..=5).min(), Some(5)); - let mut r = 10..=10; - r.next(); - assert_eq!(r.min(), None); -} - -#[test] -fn test_range_inclusive_folds() { - assert_eq!((1..=10).sum::(), 55); - assert_eq!((1..=10).rev().sum::(), 55); - - let mut it = 44..=50; - assert_eq!(it.try_fold(0, i8::checked_add), None); - assert_eq!(it, 47..=50); - assert_eq!(it.try_fold(0, i8::checked_add), None); - assert_eq!(it, 50..=50); - assert_eq!(it.try_fold(0, i8::checked_add), Some(50)); - assert!(it.is_empty()); - assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); - assert!(it.is_empty()); - - let mut it = 40..=47; - assert_eq!(it.try_rfold(0, i8::checked_add), None); - assert_eq!(it, 40..=44); - assert_eq!(it.try_rfold(0, i8::checked_add), None); - assert_eq!(it, 40..=41); - assert_eq!(it.try_rfold(0, i8::checked_add), Some(81)); - assert!(it.is_empty()); - assert_eq!(it.try_rfold(0, i8::checked_add), Some(0)); - assert!(it.is_empty()); - - let mut it = 10..=20; - assert_eq!(it.try_fold(0, |a, b| Some(a + b)), Some(165)); - assert!(it.is_empty()); - assert_eq!(it.try_fold(0, |a, b| Some(a + b)), Some(0)); - assert!(it.is_empty()); - - let mut it = 10..=20; - assert_eq!(it.try_rfold(0, |a, b| Some(a + b)), Some(165)); - assert!(it.is_empty()); - assert_eq!(it.try_rfold(0, |a, b| Some(a + b)), Some(0)); - assert!(it.is_empty()); -} - -#[test] -fn test_range_size_hint() { - assert_eq!((0..0usize).size_hint(), (0, Some(0))); - assert_eq!((0..100usize).size_hint(), (100, Some(100))); - assert_eq!((0..usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); - - let umax = u128::try_from(usize::MAX).unwrap(); - assert_eq!((0..0u128).size_hint(), (0, Some(0))); - assert_eq!((0..100u128).size_hint(), (100, Some(100))); - assert_eq!((0..umax).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((0..umax + 1).size_hint(), (usize::MAX, None)); - - assert_eq!((0..0isize).size_hint(), (0, Some(0))); - assert_eq!((-100..100isize).size_hint(), (200, Some(200))); - assert_eq!((isize::MIN..isize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); - - let imin = i128::try_from(isize::MIN).unwrap(); - let imax = i128::try_from(isize::MAX).unwrap(); - assert_eq!((0..0i128).size_hint(), (0, Some(0))); - assert_eq!((-100..100i128).size_hint(), (200, Some(200))); - assert_eq!((imin..imax).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((imin..imax + 1).size_hint(), (usize::MAX, None)); -} - -#[test] -fn test_range_inclusive_size_hint() { - assert_eq!((1..=0usize).size_hint(), (0, Some(0))); - assert_eq!((0..=0usize).size_hint(), (1, Some(1))); - assert_eq!((0..=100usize).size_hint(), (101, Some(101))); - assert_eq!((0..=usize::MAX - 1).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((0..=usize::MAX).size_hint(), (usize::MAX, None)); - - let umax = u128::try_from(usize::MAX).unwrap(); - assert_eq!((1..=0u128).size_hint(), (0, Some(0))); - assert_eq!((0..=0u128).size_hint(), (1, Some(1))); - assert_eq!((0..=100u128).size_hint(), (101, Some(101))); - assert_eq!((0..=umax - 1).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((0..=umax).size_hint(), (usize::MAX, None)); - assert_eq!((0..=umax + 1).size_hint(), (usize::MAX, None)); - - assert_eq!((0..=-1isize).size_hint(), (0, Some(0))); - assert_eq!((0..=0isize).size_hint(), (1, Some(1))); - assert_eq!((-100..=100isize).size_hint(), (201, Some(201))); - assert_eq!((isize::MIN..=isize::MAX - 1).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((isize::MIN..=isize::MAX).size_hint(), (usize::MAX, None)); - - let imin = i128::try_from(isize::MIN).unwrap(); - let imax = i128::try_from(isize::MAX).unwrap(); - assert_eq!((0..=-1i128).size_hint(), (0, Some(0))); - assert_eq!((0..=0i128).size_hint(), (1, Some(1))); - assert_eq!((-100..=100i128).size_hint(), (201, Some(201))); - assert_eq!((imin..=imax - 1).size_hint(), (usize::MAX, Some(usize::MAX))); - assert_eq!((imin..=imax).size_hint(), (usize::MAX, None)); - assert_eq!((imin..=imax + 1).size_hint(), (usize::MAX, None)); -} - -#[test] -fn test_repeat() { - let mut it = repeat(42); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(repeat(42).size_hint(), (usize::MAX, None)); -} - -#[test] -fn test_repeat_take() { - let mut it = repeat(42).take(3); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), None); - is_trusted_len(repeat(42).take(3)); - assert_eq!(repeat(42).take(3).size_hint(), (3, Some(3))); - assert_eq!(repeat(42).take(0).size_hint(), (0, Some(0))); - assert_eq!(repeat(42).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); -} - -#[test] -fn test_repeat_take_collect() { - let v: Vec<_> = repeat(42).take(3).collect(); - assert_eq!(v, vec![42, 42, 42]); -} - -#[test] -fn test_repeat_with() { - #[derive(PartialEq, Debug)] - struct NotClone(usize); - let mut it = repeat_with(|| NotClone(42)); - assert_eq!(it.next(), Some(NotClone(42))); - assert_eq!(it.next(), Some(NotClone(42))); - assert_eq!(it.next(), Some(NotClone(42))); - assert_eq!(repeat_with(|| NotClone(42)).size_hint(), (usize::MAX, None)); -} - -#[test] -fn test_repeat_with_take() { - let mut it = repeat_with(|| 42).take(3); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), None); - is_trusted_len(repeat_with(|| 42).take(3)); - assert_eq!(repeat_with(|| 42).take(3).size_hint(), (3, Some(3))); - assert_eq!(repeat_with(|| 42).take(0).size_hint(), (0, Some(0))); - assert_eq!(repeat_with(|| 42).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); -} - -#[test] -fn test_repeat_with_take_collect() { - let mut curr = 1; - let v: Vec<_> = repeat_with(|| { - let tmp = curr; - curr *= 2; - tmp - }) - .take(5) - .collect(); - assert_eq!(v, vec![1, 2, 4, 8, 16]); -} - -#[test] -fn test_successors() { - let mut powers_of_10 = successors(Some(1_u16), |n| n.checked_mul(10)); - assert_eq!(powers_of_10.by_ref().collect::>(), &[1, 10, 100, 1_000, 10_000]); - assert_eq!(powers_of_10.next(), None); - - let mut empty = successors(None::, |_| unimplemented!()); - assert_eq!(empty.next(), None); - assert_eq!(empty.next(), None); -} - -#[test] -fn test_fuse() { - let mut it = 0..3; - assert_eq!(it.len(), 3); - assert_eq!(it.next(), Some(0)); - assert_eq!(it.len(), 2); - assert_eq!(it.next(), Some(1)); - assert_eq!(it.len(), 1); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.len(), 0); - assert_eq!(it.next(), None); - assert_eq!(it.len(), 0); - assert_eq!(it.next(), None); - assert_eq!(it.len(), 0); - assert_eq!(it.next(), None); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_fuse_nth() { - let xs = [0, 1, 2]; - let mut it = xs.iter(); - - assert_eq!(it.len(), 3); - assert_eq!(it.nth(2), Some(&2)); - assert_eq!(it.len(), 0); - assert_eq!(it.nth(2), None); - assert_eq!(it.len(), 0); -} - -#[test] -fn test_fuse_last() { - let xs = [0, 1, 2]; - let it = xs.iter(); - - assert_eq!(it.len(), 3); - assert_eq!(it.last(), Some(&2)); -} - -#[test] -fn test_fuse_count() { - let xs = [0, 1, 2]; - let it = xs.iter(); - - assert_eq!(it.len(), 3); - assert_eq!(it.count(), 3); - // Can't check len now because count consumes. -} - -#[test] -fn test_fuse_fold() { - let xs = [0, 1, 2]; - let it = xs.iter(); // `FusedIterator` - let i = it.fuse().fold(0, |i, &x| { - assert_eq!(x, xs[i]); - i + 1 - }); - assert_eq!(i, xs.len()); - - let it = xs.iter(); // `FusedIterator` - let i = it.fuse().rfold(xs.len(), |i, &x| { - assert_eq!(x, xs[i - 1]); - i - 1 - }); - assert_eq!(i, 0); - - let it = xs.iter().scan((), |_, &x| Some(x)); // `!FusedIterator` - let i = it.fuse().fold(0, |i, x| { - assert_eq!(x, xs[i]); - i + 1 - }); - assert_eq!(i, xs.len()); -} - -#[test] -fn test_once() { - let mut it = once(42); - assert_eq!(it.next(), Some(42)); - assert_eq!(it.next(), None); -} - -#[test] -fn test_once_with() { - let count = Cell::new(0); - let mut it = once_with(|| { - count.set(count.get() + 1); - 42 - }); - - assert_eq!(count.get(), 0); - assert_eq!(it.next(), Some(42)); - assert_eq!(count.get(), 1); - assert_eq!(it.next(), None); - assert_eq!(count.get(), 1); - assert_eq!(it.next(), None); - assert_eq!(count.get(), 1); -} - -#[test] -fn test_empty() { - let mut it = empty::(); - assert_eq!(it.next(), None); -} - -#[test] -fn test_chain_fold() { - let xs = [1, 2, 3]; - let ys = [1, 2, 0]; - - let mut iter = xs.iter().chain(&ys); - iter.next(); - let mut result = Vec::new(); - iter.fold((), |(), &elt| result.push(elt)); - assert_eq!(&[2, 3, 1, 2, 0], &result[..]); -} - -#[test] -fn test_steps_between() { - assert_eq!(Step::steps_between(&20_u8, &200_u8), Some(180_usize)); - assert_eq!(Step::steps_between(&-20_i8, &80_i8), Some(100_usize)); - assert_eq!(Step::steps_between(&-120_i8, &80_i8), Some(200_usize)); - assert_eq!(Step::steps_between(&20_u32, &4_000_100_u32), Some(4_000_080_usize)); - assert_eq!(Step::steps_between(&-20_i32, &80_i32), Some(100_usize)); - assert_eq!(Step::steps_between(&-2_000_030_i32, &2_000_050_i32), Some(4_000_080_usize)); - - // Skip u64/i64 to avoid differences with 32-bit vs 64-bit platforms - - assert_eq!(Step::steps_between(&20_u128, &200_u128), Some(180_usize)); - assert_eq!(Step::steps_between(&-20_i128, &80_i128), Some(100_usize)); - if cfg!(target_pointer_width = "64") { - assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_0009_u128), Some(usize::MAX)); - } - assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_000a_u128), None); - assert_eq!(Step::steps_between(&10_i128, &0x1_0000_0000_0000_000a_i128), None); - assert_eq!( - Step::steps_between(&-0x1_0000_0000_0000_0000_i128, &0x1_0000_0000_0000_0000_i128,), - None, - ); -} - -#[test] -fn test_step_forward() { - assert_eq!(Step::forward_checked(55_u8, 200_usize), Some(255_u8)); - assert_eq!(Step::forward_checked(252_u8, 200_usize), None); - assert_eq!(Step::forward_checked(0_u8, 256_usize), None); - assert_eq!(Step::forward_checked(-110_i8, 200_usize), Some(90_i8)); - assert_eq!(Step::forward_checked(-110_i8, 248_usize), None); - assert_eq!(Step::forward_checked(-126_i8, 256_usize), None); - - assert_eq!(Step::forward_checked(35_u16, 100_usize), Some(135_u16)); - assert_eq!(Step::forward_checked(35_u16, 65500_usize), Some(u16::MAX)); - assert_eq!(Step::forward_checked(36_u16, 65500_usize), None); - assert_eq!(Step::forward_checked(-110_i16, 200_usize), Some(90_i16)); - assert_eq!(Step::forward_checked(-20_030_i16, 50_050_usize), Some(30_020_i16)); - assert_eq!(Step::forward_checked(-10_i16, 40_000_usize), None); - assert_eq!(Step::forward_checked(-10_i16, 70_000_usize), None); - - assert_eq!(Step::forward_checked(10_u128, 70_000_usize), Some(70_010_u128)); - assert_eq!(Step::forward_checked(10_i128, 70_030_usize), Some(70_040_i128)); - assert_eq!( - Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0xff_usize), - Some(u128::MAX), - ); - assert_eq!( - Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0x100_usize), - None - ); - assert_eq!( - Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0xff_usize), - Some(i128::MAX), - ); - assert_eq!( - Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize), - None - ); -} - -#[test] -fn test_step_backward() { - assert_eq!(Step::backward_checked(255_u8, 200_usize), Some(55_u8)); - assert_eq!(Step::backward_checked(100_u8, 200_usize), None); - assert_eq!(Step::backward_checked(255_u8, 256_usize), None); - assert_eq!(Step::backward_checked(90_i8, 200_usize), Some(-110_i8)); - assert_eq!(Step::backward_checked(110_i8, 248_usize), None); - assert_eq!(Step::backward_checked(127_i8, 256_usize), None); - - assert_eq!(Step::backward_checked(135_u16, 100_usize), Some(35_u16)); - assert_eq!(Step::backward_checked(u16::MAX, 65500_usize), Some(35_u16)); - assert_eq!(Step::backward_checked(10_u16, 11_usize), None); - assert_eq!(Step::backward_checked(90_i16, 200_usize), Some(-110_i16)); - assert_eq!(Step::backward_checked(30_020_i16, 50_050_usize), Some(-20_030_i16)); - assert_eq!(Step::backward_checked(-10_i16, 40_000_usize), None); - assert_eq!(Step::backward_checked(-10_i16, 70_000_usize), None); - - assert_eq!(Step::backward_checked(70_010_u128, 70_000_usize), Some(10_u128)); - assert_eq!(Step::backward_checked(70_020_i128, 70_030_usize), Some(-10_i128)); - assert_eq!(Step::backward_checked(10_u128, 7_usize), Some(3_u128)); - assert_eq!(Step::backward_checked(10_u128, 11_usize), None); - assert_eq!( - Step::backward_checked(-0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize), - Some(i128::MIN) - ); -} - -#[test] -fn test_rev_try_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((1..10).rev().try_fold(7, f), (1..10).try_rfold(7, f)); - assert_eq!((1..10).rev().try_rfold(7, f), (1..10).try_fold(7, f)); - - let a = [10, 20, 30, 40, 100, 60, 70, 80, 90]; - let mut iter = a.iter().rev(); - assert_eq!(iter.try_fold(0_i8, |acc, &x| acc.checked_add(x)), None); - assert_eq!(iter.next(), Some(&70)); - let mut iter = a.iter().rev(); - assert_eq!(iter.try_rfold(0_i8, |acc, &x| acc.checked_add(x)), None); - assert_eq!(iter.next_back(), Some(&60)); -} - -#[test] -fn test_cloned_try_folds() { - let a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - let f = &|acc, x| i32::checked_add(2 * acc, x); - let f_ref = &|acc, &x| i32::checked_add(2 * acc, x); - assert_eq!(a.iter().cloned().try_fold(7, f), a.iter().try_fold(7, f_ref)); - assert_eq!(a.iter().cloned().try_rfold(7, f), a.iter().try_rfold(7, f_ref)); - - let a = [10, 20, 30, 40, 100, 60, 70, 80, 90]; - let mut iter = a.iter().cloned(); - assert_eq!(iter.try_fold(0_i8, |acc, x| acc.checked_add(x)), None); - assert_eq!(iter.next(), Some(60)); - let mut iter = a.iter().cloned(); - assert_eq!(iter.try_rfold(0_i8, |acc, x| acc.checked_add(x)), None); - assert_eq!(iter.next_back(), Some(70)); -} - -#[test] -fn test_chain_try_folds() { - let c = || (0..10).chain(10..20); - - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!(c().try_fold(7, f), (0..20).try_fold(7, f)); - assert_eq!(c().try_rfold(7, f), (0..20).rev().try_fold(7, f)); - - let mut iter = c(); - assert_eq!(iter.position(|x| x == 5), Some(5)); - assert_eq!(iter.next(), Some(6), "stopped in front, state Both"); - assert_eq!(iter.position(|x| x == 13), Some(6)); - assert_eq!(iter.next(), Some(14), "stopped in back, state Back"); - assert_eq!(iter.try_fold(0, |acc, x| Some(acc + x)), Some((15..20).sum())); - - let mut iter = c().rev(); // use rev to access try_rfold - assert_eq!(iter.position(|x| x == 15), Some(4)); - assert_eq!(iter.next(), Some(14), "stopped in back, state Both"); - assert_eq!(iter.position(|x| x == 5), Some(8)); - assert_eq!(iter.next(), Some(4), "stopped in front, state Front"); - assert_eq!(iter.try_fold(0, |acc, x| Some(acc + x)), Some((0..4).sum())); - - let mut iter = c(); - iter.by_ref().rev().nth(14); // skip the last 15, ending in state Front - assert_eq!(iter.try_fold(7, f), (0..5).try_fold(7, f)); - - let mut iter = c(); - iter.nth(14); // skip the first 15, ending in state Back - assert_eq!(iter.try_rfold(7, f), (15..20).try_rfold(7, f)); -} - -#[test] -fn test_map_try_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((0..10).map(|x| x + 3).try_fold(7, f), (3..13).try_fold(7, f)); - assert_eq!((0..10).map(|x| x + 3).try_rfold(7, f), (3..13).try_rfold(7, f)); - - let mut iter = (0..40).map(|x| x + 10); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(20)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(46)); -} - -#[test] -fn test_filter_try_folds() { - fn p(&x: &i32) -> bool { - 0 <= x && x < 10 - } - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((-10..20).filter(p).try_fold(7, f), (0..10).try_fold(7, f)); - assert_eq!((-10..20).filter(p).try_rfold(7, f), (0..10).try_rfold(7, f)); - - let mut iter = (0..40).filter(|&x| x % 2 == 1); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(25)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(31)); -} - -#[test] -fn test_filter_map_try_folds() { - let mp = &|x| if 0 <= x && x < 10 { Some(x * 2) } else { None }; - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((-9..20).filter_map(mp).try_fold(7, f), (0..10).map(|x| 2 * x).try_fold(7, f)); - assert_eq!((-9..20).filter_map(mp).try_rfold(7, f), (0..10).map(|x| 2 * x).try_rfold(7, f)); - - let mut iter = (0..40).filter_map(|x| if x % 2 == 1 { None } else { Some(x * 2 + 10) }); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(38)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(78)); -} - -#[test] -fn test_enumerate_try_folds() { - let f = &|acc, (i, x)| usize::checked_add(2 * acc, x / (i + 1) + i); - assert_eq!((9..18).enumerate().try_fold(7, f), (0..9).map(|i| (i, i + 9)).try_fold(7, f)); - assert_eq!((9..18).enumerate().try_rfold(7, f), (0..9).map(|i| (i, i + 9)).try_rfold(7, f)); - - let mut iter = (100..200).enumerate(); - let f = &|acc, (i, x)| u8::checked_add(acc, u8::checked_div(x, i as u8 + 1)?); - assert_eq!(iter.try_fold(0, f), None); - assert_eq!(iter.next(), Some((7, 107))); - assert_eq!(iter.try_rfold(0, f), None); - assert_eq!(iter.next_back(), Some((11, 111))); -} - -#[test] -fn test_peek_try_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - - assert_eq!((1..20).peekable().try_fold(7, f), (1..20).try_fold(7, f)); - assert_eq!((1..20).peekable().try_rfold(7, f), (1..20).try_rfold(7, f)); - - let mut iter = (1..20).peekable(); - assert_eq!(iter.peek(), Some(&1)); - assert_eq!(iter.try_fold(7, f), (1..20).try_fold(7, f)); - - let mut iter = (1..20).peekable(); - assert_eq!(iter.peek(), Some(&1)); - assert_eq!(iter.try_rfold(7, f), (1..20).try_rfold(7, f)); - - let mut iter = [100, 20, 30, 40, 50, 60, 70].iter().cloned().peekable(); - assert_eq!(iter.peek(), Some(&100)); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.peek(), Some(&40)); - - let mut iter = [100, 20, 30, 40, 50, 60, 70].iter().cloned().peekable(); - assert_eq!(iter.peek(), Some(&100)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.peek(), Some(&100)); - assert_eq!(iter.next_back(), Some(50)); - - let mut iter = (2..5).peekable(); - assert_eq!(iter.peek(), Some(&2)); - assert_eq!(iter.try_for_each(Err), Err(2)); - assert_eq!(iter.peek(), Some(&3)); - assert_eq!(iter.try_for_each(Err), Err(3)); - assert_eq!(iter.peek(), Some(&4)); - assert_eq!(iter.try_for_each(Err), Err(4)); - assert_eq!(iter.peek(), None); - assert_eq!(iter.try_for_each(Err), Ok(())); - - let mut iter = (2..5).peekable(); - assert_eq!(iter.peek(), Some(&2)); - assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(4)); - assert_eq!(iter.peek(), Some(&2)); - assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(3)); - assert_eq!(iter.peek(), Some(&2)); - assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(2)); - assert_eq!(iter.peek(), None); - assert_eq!(iter.try_rfold((), |(), x| Err(x)), Ok(())); -} - -#[test] -fn test_skip_while_try_fold() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - fn p(&x: &i32) -> bool { - (x % 10) <= 5 - } - assert_eq!((1..20).skip_while(p).try_fold(7, f), (6..20).try_fold(7, f)); - let mut iter = (1..20).skip_while(p); - assert_eq!(iter.nth(5), Some(11)); - assert_eq!(iter.try_fold(7, f), (12..20).try_fold(7, f)); - - let mut iter = (0..50).skip_while(|&x| (x % 20) < 15); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(23)); -} - -#[test] -fn test_take_while_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((1..20).take_while(|&x| x != 10).try_fold(7, f), (1..10).try_fold(7, f)); - let mut iter = (1..20).take_while(|&x| x != 10); - assert_eq!(iter.try_fold(0, |x, y| Some(x + y)), Some((1..10).sum())); - assert_eq!(iter.next(), None, "flag should be set"); - let iter = (1..20).take_while(|&x| x != 10); - assert_eq!(iter.fold(0, |x, y| x + y), (1..10).sum()); - - let mut iter = (10..50).take_while(|&x| x != 40); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(20)); -} - -#[test] -fn test_skip_try_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((1..20).skip(9).try_fold(7, f), (10..20).try_fold(7, f)); - assert_eq!((1..20).skip(9).try_rfold(7, f), (10..20).try_rfold(7, f)); - - let mut iter = (0..30).skip(10); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(20)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(24)); -} - -#[test] -fn test_skip_nth_back() { - let xs = [0, 1, 2, 3, 4, 5]; - let mut it = xs.iter().skip(2); - assert_eq!(it.nth_back(0), Some(&5)); - assert_eq!(it.nth_back(1), Some(&3)); - assert_eq!(it.nth_back(0), Some(&2)); - assert_eq!(it.nth_back(0), None); - - let ys = [2, 3, 4, 5]; - let mut ity = ys.iter(); - let mut it = xs.iter().skip(2); - assert_eq!(it.nth_back(1), ity.nth_back(1)); - assert_eq!(it.clone().nth(0), ity.clone().nth(0)); - assert_eq!(it.nth_back(0), ity.nth_back(0)); - assert_eq!(it.clone().nth(0), ity.clone().nth(0)); - assert_eq!(it.nth_back(0), ity.nth_back(0)); - assert_eq!(it.clone().nth(0), ity.clone().nth(0)); - assert_eq!(it.nth_back(0), ity.nth_back(0)); - assert_eq!(it.clone().nth(0), ity.clone().nth(0)); - - let mut it = xs.iter().skip(2); - assert_eq!(it.nth_back(4), None); - assert_eq!(it.nth_back(0), None); - - let mut it = xs.iter(); - it.by_ref().skip(2).nth_back(3); - assert_eq!(it.next_back(), Some(&1)); - - let mut it = xs.iter(); - it.by_ref().skip(2).nth_back(10); - assert_eq!(it.next_back(), Some(&1)); -} - -#[test] -fn test_take_try_folds() { - let f = &|acc, x| i32::checked_add(2 * acc, x); - assert_eq!((10..30).take(10).try_fold(7, f), (10..20).try_fold(7, f)); - assert_eq!((10..30).take(10).try_rfold(7, f), (10..20).try_rfold(7, f)); - - let mut iter = (10..30).take(20); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(20)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(24)); - - let mut iter = (2..20).take(3); - assert_eq!(iter.try_for_each(Err), Err(2)); - assert_eq!(iter.try_for_each(Err), Err(3)); - assert_eq!(iter.try_for_each(Err), Err(4)); - assert_eq!(iter.try_for_each(Err), Ok(())); - - let mut iter = (2..20).take(3).rev(); - assert_eq!(iter.try_for_each(Err), Err(4)); - assert_eq!(iter.try_for_each(Err), Err(3)); - assert_eq!(iter.try_for_each(Err), Err(2)); - assert_eq!(iter.try_for_each(Err), Ok(())); -} - -#[test] -fn test_flat_map_try_folds() { - let f = &|acc, x| i32::checked_add(acc * 2 / 3, x); - let mr = &|x| (5 * x)..(5 * x + 5); - assert_eq!((0..10).flat_map(mr).try_fold(7, f), (0..50).try_fold(7, f)); - assert_eq!((0..10).flat_map(mr).try_rfold(7, f), (0..50).try_rfold(7, f)); - let mut iter = (0..10).flat_map(mr); - iter.next(); - iter.next_back(); // have front and back iters in progress - assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); - - let mut iter = (0..10).flat_map(|x| (4 * x)..(4 * x + 4)); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(17)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(35)); -} - -#[test] -fn test_flatten_try_folds() { - let f = &|acc, x| i32::checked_add(acc * 2 / 3, x); - let mr = &|x| (5 * x)..(5 * x + 5); - assert_eq!((0..10).map(mr).flatten().try_fold(7, f), (0..50).try_fold(7, f)); - assert_eq!((0..10).map(mr).flatten().try_rfold(7, f), (0..50).try_rfold(7, f)); - let mut iter = (0..10).map(mr).flatten(); - iter.next(); - iter.next_back(); // have front and back iters in progress - assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); - - let mut iter = (0..10).map(|x| (4 * x)..(4 * x + 4)).flatten(); - assert_eq!(iter.try_fold(0, i8::checked_add), None); - assert_eq!(iter.next(), Some(17)); - assert_eq!(iter.try_rfold(0, i8::checked_add), None); - assert_eq!(iter.next_back(), Some(35)); -} - -#[test] -fn test_functor_laws() { - // identity: - fn identity(x: T) -> T { - x - } - assert_eq!((0..10).map(identity).sum::(), (0..10).sum()); - - // composition: - fn f(x: usize) -> usize { - x + 3 - } - fn g(x: usize) -> usize { - x * 2 - } - fn h(x: usize) -> usize { - g(f(x)) - } - assert_eq!((0..10).map(f).map(g).sum::(), (0..10).map(h).sum()); -} - -#[test] -fn test_monad_laws_left_identity() { - fn f(x: usize) -> impl Iterator { - (0..10).map(move |y| x * y) - } - assert_eq!(once(42).flat_map(f.clone()).sum::(), f(42).sum()); -} - -#[test] -fn test_monad_laws_right_identity() { - assert_eq!((0..10).flat_map(|x| once(x)).sum::(), (0..10).sum()); -} - -#[test] -fn test_monad_laws_associativity() { - fn f(x: usize) -> impl Iterator { - 0..x - } - fn g(x: usize) -> impl Iterator { - (0..x).rev() - } - assert_eq!( - (0..10).flat_map(f).flat_map(g).sum::(), - (0..10).flat_map(|x| f(x).flat_map(g)).sum::() - ); -} - -#[test] -fn test_is_sorted() { - assert!([1, 2, 2, 9].iter().is_sorted()); - assert!(![1, 3, 2].iter().is_sorted()); - assert!([0].iter().is_sorted()); - assert!(std::iter::empty::().is_sorted()); - assert!(![0.0, 1.0, f32::NAN].iter().is_sorted()); - assert!([-2, -1, 0, 3].iter().is_sorted()); - assert!(![-2i32, -1, 0, 3].iter().is_sorted_by_key(|n| n.abs())); - assert!(!["c", "bb", "aaa"].iter().is_sorted()); - assert!(["c", "bb", "aaa"].iter().is_sorted_by_key(|s| s.len())); -} - -#[test] -fn test_partition() { - fn check(xs: &mut [i32], ref p: impl Fn(&i32) -> bool, expected: usize) { - let i = xs.iter_mut().partition_in_place(p); - assert_eq!(expected, i); - assert!(xs[..i].iter().all(p)); - assert!(!xs[i..].iter().any(p)); - assert!(xs.iter().is_partitioned(p)); - if i == 0 || i == xs.len() { - assert!(xs.iter().rev().is_partitioned(p)); - } else { - assert!(!xs.iter().rev().is_partitioned(p)); - } - } - - check(&mut [], |_| true, 0); - check(&mut [], |_| false, 0); - - check(&mut [0], |_| true, 1); - check(&mut [0], |_| false, 0); - - check(&mut [-1, 1], |&x| x > 0, 1); - check(&mut [-1, 1], |&x| x < 0, 1); - - let ref mut xs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - check(xs, |_| true, 10); - check(xs, |_| false, 0); - check(xs, |&x| x % 2 == 0, 5); // evens - check(xs, |&x| x % 2 == 1, 5); // odds - check(xs, |&x| x % 3 == 0, 4); // multiple of 3 - check(xs, |&x| x % 4 == 0, 3); // multiple of 4 - check(xs, |&x| x % 5 == 0, 2); // multiple of 5 - check(xs, |&x| x < 3, 3); // small - check(xs, |&x| x > 6, 3); // large -} - -/// An iterator that panics whenever `next` or next_back` is called -/// after `None` has already been returned. This does not violate -/// `Iterator`'s contract. Used to test that iterator adaptors don't -/// poll their inner iterators after exhausting them. -struct NonFused { - iter: I, - done: bool, -} - -impl NonFused { - fn new(iter: I) -> Self { - Self { iter, done: false } - } -} - -impl Iterator for NonFused -where - I: Iterator, -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - assert!(!self.done, "this iterator has already returned None"); - self.iter.next().or_else(|| { - self.done = true; - None - }) - } -} - -impl DoubleEndedIterator for NonFused -where - I: DoubleEndedIterator, -{ - fn next_back(&mut self) -> Option { - assert!(!self.done, "this iterator has already returned None"); - self.iter.next_back().or_else(|| { - self.done = true; - None - }) - } -} - -#[test] -fn test_peekable_non_fused() { - let mut iter = NonFused::new(empty::()).peekable(); - - assert_eq!(iter.peek(), None); - assert_eq!(iter.next_back(), None); -} - -#[test] -fn test_flatten_non_fused_outer() { - let mut iter = NonFused::new(once(0..2)).flatten(); - - assert_eq!(iter.next_back(), Some(1)); - assert_eq!(iter.next(), Some(0)); - assert_eq!(iter.next(), None); -} - -#[test] -fn test_flatten_non_fused_inner() { - let mut iter = once(0..1).chain(once(1..3)).flat_map(NonFused::new); - - assert_eq!(iter.next_back(), Some(2)); - assert_eq!(iter.next(), Some(0)); - assert_eq!(iter.next(), Some(1)); - assert_eq!(iter.next(), None); -} - -#[test] -pub fn extend_for_unit() { - let mut x = 0; - { - let iter = (0..5).map(|_| { - x += 1; - }); - ().extend(iter); - } - assert_eq!(x, 5); -} diff --git a/library/core/tests/iter/adapters/chain.rs b/library/core/tests/iter/adapters/chain.rs new file mode 100644 index 0000000000..ca5ae12ae2 --- /dev/null +++ b/library/core/tests/iter/adapters/chain.rs @@ -0,0 +1,272 @@ +use super::*; +use core::iter::*; + +#[test] +fn test_iterator_chain() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; + let it = xs.iter().chain(&ys); + let mut i = 0; + for &x in it { + assert_eq!(x, expected[i]); + i += 1; + } + assert_eq!(i, expected.len()); + + let ys = (30..).step_by(10).take(4); + let it = xs.iter().cloned().chain(ys); + let mut i = 0; + for x in it { + assert_eq!(x, expected[i]); + i += 1; + } + assert_eq!(i, expected.len()); +} + +#[test] +fn test_iterator_chain_advance_by() { + fn test_chain(xs: &[i32], ys: &[i32]) { + let len = xs.len() + ys.len(); + + for i in 0..xs.len() { + let mut iter = Unfuse::new(xs).chain(Unfuse::new(ys)); + iter.advance_by(i).unwrap(); + assert_eq!(iter.next(), Some(&xs[i])); + assert_eq!(iter.advance_by(100), Err(len - i - 1)); + } + + for i in 0..ys.len() { + let mut iter = Unfuse::new(xs).chain(Unfuse::new(ys)); + iter.advance_by(xs.len() + i).unwrap(); + assert_eq!(iter.next(), Some(&ys[i])); + assert_eq!(iter.advance_by(100), Err(ys.len() - i - 1)); + } + + let mut iter = xs.iter().chain(ys); + iter.advance_by(len).unwrap(); + assert_eq!(iter.next(), None); + + let mut iter = xs.iter().chain(ys); + assert_eq!(iter.advance_by(len + 1), Err(len)); + } + + test_chain(&[], &[]); + test_chain(&[], &[0, 1, 2, 3, 4, 5]); + test_chain(&[0, 1, 2, 3, 4, 5], &[]); + test_chain(&[0, 1, 2, 3, 4, 5], &[30, 40, 50, 60]); +} + +#[test] +fn test_iterator_chain_advance_back_by() { + fn test_chain(xs: &[i32], ys: &[i32]) { + let len = xs.len() + ys.len(); + + for i in 0..ys.len() { + let mut iter = Unfuse::new(xs).chain(Unfuse::new(ys)); + iter.advance_back_by(i).unwrap(); + assert_eq!(iter.next_back(), Some(&ys[ys.len() - i - 1])); + assert_eq!(iter.advance_back_by(100), Err(len - i - 1)); + } + + for i in 0..xs.len() { + let mut iter = Unfuse::new(xs).chain(Unfuse::new(ys)); + iter.advance_back_by(ys.len() + i).unwrap(); + assert_eq!(iter.next_back(), Some(&xs[xs.len() - i - 1])); + assert_eq!(iter.advance_back_by(100), Err(xs.len() - i - 1)); + } + + let mut iter = xs.iter().chain(ys); + iter.advance_back_by(len).unwrap(); + assert_eq!(iter.next_back(), None); + + let mut iter = xs.iter().chain(ys); + assert_eq!(iter.advance_back_by(len + 1), Err(len)); + } + + test_chain(&[], &[]); + test_chain(&[], &[0, 1, 2, 3, 4, 5]); + test_chain(&[0, 1, 2, 3, 4, 5], &[]); + test_chain(&[0, 1, 2, 3, 4, 5], &[30, 40, 50, 60]); +} + +#[test] +fn test_iterator_chain_nth() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let zs = []; + let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; + for (i, x) in expected.iter().enumerate() { + assert_eq!(Some(x), xs.iter().chain(&ys).nth(i)); + } + assert_eq!(zs.iter().chain(&xs).nth(0), Some(&0)); + + let mut it = xs.iter().chain(&zs); + assert_eq!(it.nth(5), Some(&5)); + assert_eq!(it.next(), None); +} + +#[test] +fn test_iterator_chain_nth_back() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let zs = []; + let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; + for (i, x) in expected.iter().rev().enumerate() { + assert_eq!(Some(x), xs.iter().chain(&ys).nth_back(i)); + } + assert_eq!(zs.iter().chain(&xs).nth_back(0), Some(&5)); + + let mut it = xs.iter().chain(&zs); + assert_eq!(it.nth_back(5), Some(&0)); + assert_eq!(it.next(), None); +} + +#[test] +fn test_iterator_chain_last() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let zs = []; + assert_eq!(xs.iter().chain(&ys).last(), Some(&60)); + assert_eq!(zs.iter().chain(&ys).last(), Some(&60)); + assert_eq!(ys.iter().chain(&zs).last(), Some(&60)); + assert_eq!(zs.iter().chain(&zs).last(), None); +} + +#[test] +fn test_iterator_chain_count() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let zs = []; + assert_eq!(xs.iter().chain(&ys).count(), 10); + assert_eq!(zs.iter().chain(&ys).count(), 4); +} + +#[test] +fn test_iterator_chain_find() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let mut iter = xs.iter().chain(&ys); + assert_eq!(iter.find(|&&i| i == 4), Some(&4)); + assert_eq!(iter.next(), Some(&5)); + assert_eq!(iter.find(|&&i| i == 40), Some(&40)); + assert_eq!(iter.next(), Some(&50)); + assert_eq!(iter.find(|&&i| i == 100), None); + assert_eq!(iter.next(), None); +} + +#[test] +fn test_iterator_chain_size_hint() { + // this chains an iterator of length 0 with an iterator of length 1, + // so after calling `.next()` once, the iterator is empty and the + // state is `ChainState::Back`. `.size_hint()` should now disregard + // the size hint of the left iterator + let mut iter = Toggle { is_empty: true }.chain(once(())); + assert_eq!(iter.next(), Some(())); + assert_eq!(iter.size_hint(), (0, Some(0))); + + let mut iter = once(()).chain(Toggle { is_empty: true }); + assert_eq!(iter.next_back(), Some(())); + assert_eq!(iter.size_hint(), (0, Some(0))); +} + +#[test] +fn test_iterator_chain_unfused() { + // Chain shouldn't be fused in its second iterator, depending on direction + let mut iter = NonFused::new(empty()).chain(Toggle { is_empty: true }); + iter.next().unwrap_none(); + iter.next().unwrap(); + iter.next().unwrap_none(); + + let mut iter = Toggle { is_empty: true }.chain(NonFused::new(empty())); + iter.next_back().unwrap_none(); + iter.next_back().unwrap(); + iter.next_back().unwrap_none(); +} + +#[test] +fn test_chain_fold() { + let xs = [1, 2, 3]; + let ys = [1, 2, 0]; + + let mut iter = xs.iter().chain(&ys); + iter.next(); + let mut result = Vec::new(); + iter.fold((), |(), &elt| result.push(elt)); + assert_eq!(&[2, 3, 1, 2, 0], &result[..]); +} + +#[test] +fn test_chain_try_folds() { + let c = || (0..10).chain(10..20); + + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!(c().try_fold(7, f), (0..20).try_fold(7, f)); + assert_eq!(c().try_rfold(7, f), (0..20).rev().try_fold(7, f)); + + let mut iter = c(); + assert_eq!(iter.position(|x| x == 5), Some(5)); + assert_eq!(iter.next(), Some(6), "stopped in front, state Both"); + assert_eq!(iter.position(|x| x == 13), Some(6)); + assert_eq!(iter.next(), Some(14), "stopped in back, state Back"); + assert_eq!(iter.try_fold(0, |acc, x| Some(acc + x)), Some((15..20).sum())); + + let mut iter = c().rev(); // use rev to access try_rfold + assert_eq!(iter.position(|x| x == 15), Some(4)); + assert_eq!(iter.next(), Some(14), "stopped in back, state Both"); + assert_eq!(iter.position(|x| x == 5), Some(8)); + assert_eq!(iter.next(), Some(4), "stopped in front, state Front"); + assert_eq!(iter.try_fold(0, |acc, x| Some(acc + x)), Some((0..4).sum())); + + let mut iter = c(); + iter.by_ref().rev().nth(14); // skip the last 15, ending in state Front + assert_eq!(iter.try_fold(7, f), (0..5).try_fold(7, f)); + + let mut iter = c(); + iter.nth(14); // skip the first 15, ending in state Back + assert_eq!(iter.try_rfold(7, f), (15..20).try_rfold(7, f)); +} + +#[test] +fn test_double_ended_chain() { + let xs = [1, 2, 3, 4, 5]; + let ys = [7, 9, 11]; + let mut it = xs.iter().chain(&ys).rev(); + assert_eq!(it.next().unwrap(), &11); + assert_eq!(it.next().unwrap(), &9); + assert_eq!(it.next_back().unwrap(), &1); + assert_eq!(it.next_back().unwrap(), &2); + assert_eq!(it.next_back().unwrap(), &3); + assert_eq!(it.next_back().unwrap(), &4); + assert_eq!(it.next_back().unwrap(), &5); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back(), None); + + // test that .chain() is well behaved with an unfused iterator + struct CrazyIterator(bool); + impl CrazyIterator { + fn new() -> CrazyIterator { + CrazyIterator(false) + } + } + impl Iterator for CrazyIterator { + type Item = i32; + fn next(&mut self) -> Option { + if self.0 { + Some(99) + } else { + self.0 = true; + None + } + } + } + + impl DoubleEndedIterator for CrazyIterator { + fn next_back(&mut self) -> Option { + self.next() + } + } + + assert_eq!(CrazyIterator::new().chain(0..10).rev().last(), Some(0)); + assert!((0..10).chain(CrazyIterator::new()).rev().any(|i| i == 0)); +} diff --git a/library/core/tests/iter/adapters/cloned.rs b/library/core/tests/iter/adapters/cloned.rs new file mode 100644 index 0000000000..78babb7fea --- /dev/null +++ b/library/core/tests/iter/adapters/cloned.rs @@ -0,0 +1,52 @@ +use core::iter::*; + +#[test] +fn test_cloned() { + let xs = [2, 4, 6, 8]; + + let mut it = xs.iter().cloned(); + assert_eq!(it.len(), 4); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.len(), 3); + assert_eq!(it.next(), Some(4)); + assert_eq!(it.len(), 2); + assert_eq!(it.next_back(), Some(8)); + assert_eq!(it.len(), 1); + assert_eq!(it.next_back(), Some(6)); + assert_eq!(it.len(), 0); + assert_eq!(it.next_back(), None); +} + +#[test] +fn test_cloned_side_effects() { + let mut count = 0; + { + let iter = [1, 2, 3] + .iter() + .map(|x| { + count += 1; + x + }) + .cloned() + .zip(&[1]); + for _ in iter {} + } + assert_eq!(count, 2); +} + +#[test] +fn test_cloned_try_folds() { + let a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let f = &|acc, x| i32::checked_add(2 * acc, x); + let f_ref = &|acc, &x| i32::checked_add(2 * acc, x); + assert_eq!(a.iter().cloned().try_fold(7, f), a.iter().try_fold(7, f_ref)); + assert_eq!(a.iter().cloned().try_rfold(7, f), a.iter().try_rfold(7, f_ref)); + + let a = [10, 20, 30, 40, 100, 60, 70, 80, 90]; + let mut iter = a.iter().cloned(); + assert_eq!(iter.try_fold(0_i8, |acc, x| acc.checked_add(x)), None); + assert_eq!(iter.next(), Some(60)); + let mut iter = a.iter().cloned(); + assert_eq!(iter.try_rfold(0_i8, |acc, x| acc.checked_add(x)), None); + assert_eq!(iter.next_back(), Some(70)); +} diff --git a/library/core/tests/iter/adapters/copied.rs b/library/core/tests/iter/adapters/copied.rs new file mode 100644 index 0000000000..b12f2035dc --- /dev/null +++ b/library/core/tests/iter/adapters/copied.rs @@ -0,0 +1,18 @@ +use core::iter::*; + +#[test] +fn test_copied() { + let xs = [2, 4, 6, 8]; + + let mut it = xs.iter().copied(); + assert_eq!(it.len(), 4); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.len(), 3); + assert_eq!(it.next(), Some(4)); + assert_eq!(it.len(), 2); + assert_eq!(it.next_back(), Some(8)); + assert_eq!(it.len(), 1); + assert_eq!(it.next_back(), Some(6)); + assert_eq!(it.len(), 0); + assert_eq!(it.next_back(), None); +} diff --git a/library/core/tests/iter/adapters/cycle.rs b/library/core/tests/iter/adapters/cycle.rs new file mode 100644 index 0000000000..8831c09b48 --- /dev/null +++ b/library/core/tests/iter/adapters/cycle.rs @@ -0,0 +1,31 @@ +use core::iter::*; + +#[test] +fn test_cycle() { + let cycle_len = 3; + let it = (0..).step_by(1).take(cycle_len).cycle(); + assert_eq!(it.size_hint(), (usize::MAX, None)); + for (i, x) in it.take(100).enumerate() { + assert_eq!(i % cycle_len, x); + } + + let mut it = (0..).step_by(1).take(0).cycle(); + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next(), None); + + assert_eq!(empty::().cycle().fold(0, |acc, x| acc + x), 0); + + assert_eq!(once(1).cycle().skip(1).take(4).fold(0, |acc, x| acc + x), 4); + + assert_eq!((0..10).cycle().take(5).sum::(), 10); + assert_eq!((0..10).cycle().take(15).sum::(), 55); + assert_eq!((0..10).cycle().take(25).sum::(), 100); + + let mut iter = (0..10).cycle(); + iter.nth(14); + assert_eq!(iter.take(8).sum::(), 38); + + let mut iter = (0..10).cycle(); + iter.nth(9); + assert_eq!(iter.take(3).sum::(), 3); +} diff --git a/library/core/tests/iter/adapters/enumerate.rs b/library/core/tests/iter/adapters/enumerate.rs new file mode 100644 index 0000000000..0e60338784 --- /dev/null +++ b/library/core/tests/iter/adapters/enumerate.rs @@ -0,0 +1,107 @@ +use core::iter::*; + +#[test] +fn test_iterator_enumerate() { + let xs = [0, 1, 2, 3, 4, 5]; + let it = xs.iter().enumerate(); + for (i, &x) in it { + assert_eq!(i, x); + } +} + +#[test] +fn test_iterator_enumerate_nth() { + let xs = [0, 1, 2, 3, 4, 5]; + for (i, &x) in xs.iter().enumerate() { + assert_eq!(i, x); + } + + let mut it = xs.iter().enumerate(); + while let Some((i, &x)) = it.nth(0) { + assert_eq!(i, x); + } + + let mut it = xs.iter().enumerate(); + while let Some((i, &x)) = it.nth(1) { + assert_eq!(i, x); + } + + let (i, &x) = xs.iter().enumerate().nth(3).unwrap(); + assert_eq!(i, x); + assert_eq!(i, 3); +} + +#[test] +fn test_iterator_enumerate_nth_back() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().enumerate(); + while let Some((i, &x)) = it.nth_back(0) { + assert_eq!(i, x); + } + + let mut it = xs.iter().enumerate(); + while let Some((i, &x)) = it.nth_back(1) { + assert_eq!(i, x); + } + + let (i, &x) = xs.iter().enumerate().nth_back(3).unwrap(); + assert_eq!(i, x); + assert_eq!(i, 2); +} + +#[test] +fn test_iterator_enumerate_count() { + let xs = [0, 1, 2, 3, 4, 5]; + assert_eq!(xs.iter().enumerate().count(), 6); +} + +#[test] +fn test_iterator_enumerate_fold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().enumerate(); + // steal a couple to get an interesting offset + assert_eq!(it.next(), Some((0, &0))); + assert_eq!(it.next(), Some((1, &1))); + let i = it.fold(2, |i, (j, &x)| { + assert_eq!(i, j); + assert_eq!(x, xs[j]); + i + 1 + }); + assert_eq!(i, xs.len()); + + let mut it = xs.iter().enumerate(); + assert_eq!(it.next(), Some((0, &0))); + let i = it.rfold(xs.len() - 1, |i, (j, &x)| { + assert_eq!(i, j); + assert_eq!(x, xs[j]); + i - 1 + }); + assert_eq!(i, 0); +} + +#[test] +fn test_enumerate_try_folds() { + let f = &|acc, (i, x)| usize::checked_add(2 * acc, x / (i + 1) + i); + assert_eq!((9..18).enumerate().try_fold(7, f), (0..9).map(|i| (i, i + 9)).try_fold(7, f)); + assert_eq!((9..18).enumerate().try_rfold(7, f), (0..9).map(|i| (i, i + 9)).try_rfold(7, f)); + + let mut iter = (100..200).enumerate(); + let f = &|acc, (i, x)| u8::checked_add(acc, u8::checked_div(x, i as u8 + 1)?); + assert_eq!(iter.try_fold(0, f), None); + assert_eq!(iter.next(), Some((7, 107))); + assert_eq!(iter.try_rfold(0, f), None); + assert_eq!(iter.next_back(), Some((11, 111))); +} + +#[test] +fn test_double_ended_enumerate() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut it = xs.iter().cloned().enumerate(); + assert_eq!(it.next(), Some((0, 1))); + assert_eq!(it.next(), Some((1, 2))); + assert_eq!(it.next_back(), Some((5, 6))); + assert_eq!(it.next_back(), Some((4, 5))); + assert_eq!(it.next_back(), Some((3, 4))); + assert_eq!(it.next_back(), Some((2, 3))); + assert_eq!(it.next(), None); +} diff --git a/library/core/tests/iter/adapters/filter.rs b/library/core/tests/iter/adapters/filter.rs new file mode 100644 index 0000000000..a2050d89d8 --- /dev/null +++ b/library/core/tests/iter/adapters/filter.rs @@ -0,0 +1,52 @@ +use core::iter::*; + +#[test] +fn test_iterator_filter_count() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(xs.iter().filter(|&&x| x % 2 == 0).count(), 5); +} + +#[test] +fn test_iterator_filter_fold() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let ys = [0, 2, 4, 6, 8]; + let it = xs.iter().filter(|&&x| x % 2 == 0); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().filter(|&&x| x % 2 == 0); + let i = it.rfold(ys.len(), |i, &x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + +#[test] +fn test_filter_try_folds() { + fn p(&x: &i32) -> bool { + 0 <= x && x < 10 + } + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((-10..20).filter(p).try_fold(7, f), (0..10).try_fold(7, f)); + assert_eq!((-10..20).filter(p).try_rfold(7, f), (0..10).try_rfold(7, f)); + + let mut iter = (0..40).filter(|&x| x % 2 == 1); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(25)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(31)); +} + +#[test] +fn test_double_ended_filter() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut it = xs.iter().filter(|&x| *x & 1 == 0); + assert_eq!(it.next_back().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &4); + assert_eq!(it.next().unwrap(), &2); + assert_eq!(it.next_back(), None); +} diff --git a/library/core/tests/iter/adapters/filter_map.rs b/library/core/tests/iter/adapters/filter_map.rs new file mode 100644 index 0000000000..46738eda63 --- /dev/null +++ b/library/core/tests/iter/adapters/filter_map.rs @@ -0,0 +1,50 @@ +use core::iter::*; + +#[test] +fn test_filter_map() { + let it = (0..).step_by(1).take(10).filter_map(|x| if x % 2 == 0 { Some(x * x) } else { None }); + assert_eq!(it.collect::>(), [0 * 0, 2 * 2, 4 * 4, 6 * 6, 8 * 8]); +} + +#[test] +fn test_filter_map_fold() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let ys = [0 * 0, 2 * 2, 4 * 4, 6 * 6, 8 * 8]; + let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + +#[test] +fn test_filter_map_try_folds() { + let mp = &|x| if 0 <= x && x < 10 { Some(x * 2) } else { None }; + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((-9..20).filter_map(mp).try_fold(7, f), (0..10).map(|x| 2 * x).try_fold(7, f)); + assert_eq!((-9..20).filter_map(mp).try_rfold(7, f), (0..10).map(|x| 2 * x).try_rfold(7, f)); + + let mut iter = (0..40).filter_map(|x| if x % 2 == 1 { None } else { Some(x * 2 + 10) }); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(38)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(78)); +} + +#[test] +fn test_double_ended_filter_map() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut it = xs.iter().filter_map(|&x| if x & 1 == 0 { Some(x * 2) } else { None }); + assert_eq!(it.next_back().unwrap(), 12); + assert_eq!(it.next_back().unwrap(), 8); + assert_eq!(it.next().unwrap(), 4); + assert_eq!(it.next_back(), None); +} diff --git a/library/core/tests/iter/adapters/flat_map.rs b/library/core/tests/iter/adapters/flat_map.rs new file mode 100644 index 0000000000..ee945e6980 --- /dev/null +++ b/library/core/tests/iter/adapters/flat_map.rs @@ -0,0 +1,74 @@ +use core::iter::*; + +#[test] +fn test_iterator_flat_map() { + let xs = [0, 3, 6]; + let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let it = xs.iter().flat_map(|&x| (x..).step_by(1).take(3)); + let mut i = 0; + for x in it { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +/// Tests `FlatMap::fold` with items already picked off the front and back, +/// to make sure all parts of the `FlatMap` are folded correctly. +#[test] +fn test_iterator_flat_map_fold() { + let xs = [0, 3, 6]; + let ys = [1, 2, 3, 4, 5, 6, 7]; + let mut it = xs.iter().flat_map(|&x| x..x + 3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().flat_map(|&x| x..x + 3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + +#[test] +fn test_flat_map_try_folds() { + let f = &|acc, x| i32::checked_add(acc * 2 / 3, x); + let mr = &|x| (5 * x)..(5 * x + 5); + assert_eq!((0..10).flat_map(mr).try_fold(7, f), (0..50).try_fold(7, f)); + assert_eq!((0..10).flat_map(mr).try_rfold(7, f), (0..50).try_rfold(7, f)); + let mut iter = (0..10).flat_map(mr); + iter.next(); + iter.next_back(); // have front and back iters in progress + assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); + + let mut iter = (0..10).flat_map(|x| (4 * x)..(4 * x + 4)); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(17)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(35)); +} + +#[test] +fn test_double_ended_flat_map() { + let u = [0, 1]; + let v = [5, 6, 7, 8]; + let mut it = u.iter().flat_map(|x| &v[*x..v.len()]); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &5); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next_back(), None); +} diff --git a/library/core/tests/iter/adapters/flatten.rs b/library/core/tests/iter/adapters/flatten.rs new file mode 100644 index 0000000000..4bbae6947b --- /dev/null +++ b/library/core/tests/iter/adapters/flatten.rs @@ -0,0 +1,111 @@ +use super::*; +use core::iter::*; + +#[test] +fn test_iterator_flatten() { + let xs = [0, 3, 6]; + let ys = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let it = xs.iter().map(|&x| (x..).step_by(1).take(3)).flatten(); + let mut i = 0; + for x in it { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +/// Tests `Flatten::fold` with items already picked off the front and back, +/// to make sure all parts of the `Flatten` are folded correctly. +#[test] +fn test_iterator_flatten_fold() { + let xs = [0, 3, 6]; + let ys = [1, 2, 3, 4, 5, 6, 7]; + let mut it = xs.iter().map(|&x| x..x + 3).flatten(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().map(|&x| x..x + 3).flatten(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + +#[test] +fn test_flatten_try_folds() { + let f = &|acc, x| i32::checked_add(acc * 2 / 3, x); + let mr = &|x| (5 * x)..(5 * x + 5); + assert_eq!((0..10).map(mr).flatten().try_fold(7, f), (0..50).try_fold(7, f)); + assert_eq!((0..10).map(mr).flatten().try_rfold(7, f), (0..50).try_rfold(7, f)); + let mut iter = (0..10).map(mr).flatten(); + iter.next(); + iter.next_back(); // have front and back iters in progress + assert_eq!(iter.try_rfold(7, f), (1..49).try_rfold(7, f)); + + let mut iter = (0..10).map(|x| (4 * x)..(4 * x + 4)).flatten(); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(17)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(35)); +} + +#[test] +fn test_flatten_non_fused_outer() { + let mut iter = NonFused::new(once(0..2)).flatten(); + + assert_eq!(iter.next_back(), Some(1)); + assert_eq!(iter.next(), Some(0)); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + + let mut iter = NonFused::new(once(0..2)).flatten(); + + assert_eq!(iter.next(), Some(0)); + assert_eq!(iter.next_back(), Some(1)); + assert_eq!(iter.next_back(), None); + assert_eq!(iter.next_back(), None); +} + +#[test] +fn test_flatten_non_fused_inner() { + let mut iter = once(0..1).chain(once(1..3)).flat_map(NonFused::new); + + assert_eq!(iter.next_back(), Some(2)); + assert_eq!(iter.next(), Some(0)); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + + let mut iter = once(0..1).chain(once(1..3)).flat_map(NonFused::new); + + assert_eq!(iter.next(), Some(0)); + assert_eq!(iter.next_back(), Some(2)); + assert_eq!(iter.next_back(), Some(1)); + assert_eq!(iter.next_back(), None); + assert_eq!(iter.next_back(), None); +} + +#[test] +fn test_double_ended_flatten() { + let u = [0, 1]; + let v = [5, 6, 7, 8]; + let mut it = u.iter().map(|x| &v[*x..v.len()]).flatten(); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &5); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &8); + assert_eq!(it.next().unwrap(), &6); + assert_eq!(it.next_back().unwrap(), &7); + assert_eq!(it.next_back(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next_back(), None); +} diff --git a/library/core/tests/iter/adapters/fuse.rs b/library/core/tests/iter/adapters/fuse.rs new file mode 100644 index 0000000000..f41b379b3a --- /dev/null +++ b/library/core/tests/iter/adapters/fuse.rs @@ -0,0 +1,75 @@ +use core::iter::*; + +#[test] +fn test_fuse_nth() { + let xs = [0, 1, 2]; + let mut it = xs.iter(); + + assert_eq!(it.len(), 3); + assert_eq!(it.nth(2), Some(&2)); + assert_eq!(it.len(), 0); + assert_eq!(it.nth(2), None); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_fuse_last() { + let xs = [0, 1, 2]; + let it = xs.iter(); + + assert_eq!(it.len(), 3); + assert_eq!(it.last(), Some(&2)); +} + +#[test] +fn test_fuse_count() { + let xs = [0, 1, 2]; + let it = xs.iter(); + + assert_eq!(it.len(), 3); + assert_eq!(it.count(), 3); + // Can't check len now because count consumes. +} + +#[test] +fn test_fuse_fold() { + let xs = [0, 1, 2]; + let it = xs.iter(); // `FusedIterator` + let i = it.fuse().fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); + + let it = xs.iter(); // `FusedIterator` + let i = it.fuse().rfold(xs.len(), |i, &x| { + assert_eq!(x, xs[i - 1]); + i - 1 + }); + assert_eq!(i, 0); + + let it = xs.iter().scan((), |_, &x| Some(x)); // `!FusedIterator` + let i = it.fuse().fold(0, |i, x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + +#[test] +fn test_fuse() { + let mut it = 0..3; + assert_eq!(it.len(), 3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.len(), 2); + assert_eq!(it.next(), Some(1)); + assert_eq!(it.len(), 1); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.len(), 0); + assert_eq!(it.next(), None); + assert_eq!(it.len(), 0); + assert_eq!(it.next(), None); + assert_eq!(it.len(), 0); + assert_eq!(it.next(), None); + assert_eq!(it.len(), 0); +} diff --git a/library/core/tests/iter/adapters/inspect.rs b/library/core/tests/iter/adapters/inspect.rs new file mode 100644 index 0000000000..939e3a28a7 --- /dev/null +++ b/library/core/tests/iter/adapters/inspect.rs @@ -0,0 +1,38 @@ +use core::iter::*; + +#[test] +fn test_inspect() { + let xs = [1, 2, 3, 4]; + let mut n = 0; + + let ys = xs.iter().cloned().inspect(|_| n += 1).collect::>(); + + assert_eq!(n, xs.len()); + assert_eq!(&xs[..], &ys[..]); +} + +#[test] +fn test_inspect_fold() { + let xs = [1, 2, 3, 4]; + let mut n = 0; + { + let it = xs.iter().inspect(|_| n += 1); + let i = it.fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); + } + assert_eq!(n, xs.len()); + + let mut n = 0; + { + let it = xs.iter().inspect(|_| n += 1); + let i = it.rfold(xs.len(), |i, &x| { + assert_eq!(x, xs[i - 1]); + i - 1 + }); + assert_eq!(i, 0); + } + assert_eq!(n, xs.len()); +} diff --git a/library/core/tests/iter/adapters/intersperse.rs b/library/core/tests/iter/adapters/intersperse.rs new file mode 100644 index 0000000000..9dbe232e4e --- /dev/null +++ b/library/core/tests/iter/adapters/intersperse.rs @@ -0,0 +1,154 @@ +use core::iter::*; + +#[test] +fn test_intersperse() { + let v = std::iter::empty().intersperse(0u32).collect::>(); + assert_eq!(v, vec![]); + + let v = std::iter::once(1).intersperse(0).collect::>(); + assert_eq!(v, vec![1]); + + let xs = ["a", "", "b", "c"]; + let v: Vec<&str> = xs.iter().map(|x| x.clone()).intersperse(", ").collect(); + let text: String = v.concat(); + assert_eq!(text, "a, , b, c".to_string()); + + let ys = [0, 1, 2, 3]; + let mut it = ys[..0].iter().map(|x| *x).intersperse(1); + assert!(it.next() == None); +} + +#[test] +fn test_intersperse_size_hint() { + let iter = std::iter::empty::().intersperse(0); + assert_eq!(iter.size_hint(), (0, Some(0))); + + let xs = ["a", "", "b", "c"]; + let mut iter = xs.iter().map(|x| x.clone()).intersperse(", "); + assert_eq!(iter.size_hint(), (7, Some(7))); + + assert_eq!(iter.next(), Some("a")); + assert_eq!(iter.size_hint(), (6, Some(6))); + assert_eq!(iter.next(), Some(", ")); + assert_eq!(iter.size_hint(), (5, Some(5))); + + assert_eq!([].iter().intersperse(&()).size_hint(), (0, Some(0))); +} + +#[test] +fn test_fold_specialization_intersperse() { + let mut iter = (1..2).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); + + let mut iter = (1..3).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); + + let mut iter = (1..4).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); +} + +#[test] +fn test_try_fold_specialization_intersperse_ok() { + let mut iter = (1..2).intersperse(0); + iter.clone().try_for_each(|x| { + assert_eq!(Some(x), iter.next()); + Some(()) + }); + + let mut iter = (1..3).intersperse(0); + iter.clone().try_for_each(|x| { + assert_eq!(Some(x), iter.next()); + Some(()) + }); + + let mut iter = (1..4).intersperse(0); + iter.clone().try_for_each(|x| { + assert_eq!(Some(x), iter.next()); + Some(()) + }); +} + +#[test] +fn test_intersperse_with() { + #[derive(PartialEq, Debug)] + struct NotClone { + u: u32, + } + let r = vec![NotClone { u: 0 }, NotClone { u: 1 }] + .into_iter() + .intersperse_with(|| NotClone { u: 2 }) + .collect::>(); + assert_eq!(r, vec![NotClone { u: 0 }, NotClone { u: 2 }, NotClone { u: 1 }]); + + let mut ctr = 100; + let separator = || { + ctr *= 2; + ctr + }; + let r = (0..3).intersperse_with(separator).collect::>(); + assert_eq!(r, vec![0, 200, 1, 400, 2]); +} + +#[test] +fn test_intersperse_fold() { + let v = (1..4).intersperse(9).fold(Vec::new(), |mut acc, x| { + acc.push(x); + acc + }); + assert_eq!(v.as_slice(), [1, 9, 2, 9, 3]); + + let mut iter = (1..4).intersperse(9); + assert_eq!(iter.next(), Some(1)); + let v = iter.fold(Vec::new(), |mut acc, x| { + acc.push(x); + acc + }); + assert_eq!(v.as_slice(), [9, 2, 9, 3]); + + struct NoneAtStart(i32); // Produces: None, Some(2), Some(3), None, ... + impl Iterator for NoneAtStart { + type Item = i32; + fn next(&mut self) -> Option { + self.0 += 1; + Some(self.0).filter(|i| i % 3 != 1) + } + } + + let v = NoneAtStart(0).intersperse(1000).fold(0, |a, b| a + b); + assert_eq!(v, 0); +} + +#[test] +fn test_intersperse_collect_string() { + let contents = vec![1, 2, 3]; + + let contents_string = contents + .into_iter() + .map(|id| id.to_string()) + .intersperse(", ".to_owned()) + .collect::(); + assert_eq!(contents_string, "1, 2, 3"); +} + +#[test] +fn test_try_fold_specialization_intersperse_err() { + let orig_iter = ["a", "b"].iter().copied().intersperse("-"); + + // Abort after the first item. + let mut iter = orig_iter.clone(); + iter.try_for_each(|_| None::<()>); + assert_eq!(iter.next(), Some("-")); + assert_eq!(iter.next(), Some("b")); + assert_eq!(iter.next(), None); + + // Abort after the second item. + let mut iter = orig_iter.clone(); + iter.try_for_each(|item| if item == "-" { None } else { Some(()) }); + assert_eq!(iter.next(), Some("b")); + assert_eq!(iter.next(), None); + + // Abort after the third item. + let mut iter = orig_iter.clone(); + iter.try_for_each(|item| if item == "b" { None } else { Some(()) }); + assert_eq!(iter.next(), None); +} diff --git a/library/core/tests/iter/adapters/map.rs b/library/core/tests/iter/adapters/map.rs new file mode 100644 index 0000000000..77ce3819b3 --- /dev/null +++ b/library/core/tests/iter/adapters/map.rs @@ -0,0 +1,27 @@ +use core::iter::*; + +#[test] +fn test_map_try_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((0..10).map(|x| x + 3).try_fold(7, f), (3..13).try_fold(7, f)); + assert_eq!((0..10).map(|x| x + 3).try_rfold(7, f), (3..13).try_rfold(7, f)); + + let mut iter = (0..40).map(|x| x + 10); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(46)); +} + +#[test] +fn test_double_ended_map() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut it = xs.iter().map(|&x| x * -1); + assert_eq!(it.next(), Some(-1)); + assert_eq!(it.next(), Some(-2)); + assert_eq!(it.next_back(), Some(-6)); + assert_eq!(it.next_back(), Some(-5)); + assert_eq!(it.next(), Some(-3)); + assert_eq!(it.next_back(), Some(-4)); + assert_eq!(it.next(), None); +} diff --git a/library/core/tests/iter/adapters/mod.rs b/library/core/tests/iter/adapters/mod.rs new file mode 100644 index 0000000000..96a53be1ea --- /dev/null +++ b/library/core/tests/iter/adapters/mod.rs @@ -0,0 +1,185 @@ +mod chain; +mod cloned; +mod copied; +mod cycle; +mod enumerate; +mod filter; +mod filter_map; +mod flat_map; +mod flatten; +mod fuse; +mod inspect; +mod intersperse; +mod map; +mod peekable; +mod scan; +mod skip; +mod skip_while; +mod step_by; +mod take; +mod take_while; +mod zip; + +use core::cell::Cell; + +/// An iterator that panics whenever `next` or next_back` is called +/// after `None` has already been returned. This does not violate +/// `Iterator`'s contract. Used to test that iterator adaptors don't +/// poll their inner iterators after exhausting them. +pub struct NonFused { + iter: I, + done: bool, +} + +impl NonFused { + pub fn new(iter: I) -> Self { + Self { iter, done: false } + } +} + +impl Iterator for NonFused +where + I: Iterator, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + assert!(!self.done, "this iterator has already returned None"); + self.iter.next().or_else(|| { + self.done = true; + None + }) + } +} + +impl DoubleEndedIterator for NonFused +where + I: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option { + assert!(!self.done, "this iterator has already returned None"); + self.iter.next_back().or_else(|| { + self.done = true; + None + }) + } +} + +/// An iterator wrapper that panics whenever `next` or `next_back` is called +/// after `None` has been returned. +pub struct Unfuse { + iter: I, + exhausted: bool, +} + +impl Unfuse { + pub fn new(iter: T) -> Self + where + T: IntoIterator, + { + Self { iter: iter.into_iter(), exhausted: false } + } +} + +impl Iterator for Unfuse +where + I: Iterator, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + assert!(!self.exhausted); + let next = self.iter.next(); + self.exhausted = next.is_none(); + next + } +} + +impl DoubleEndedIterator for Unfuse +where + I: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option { + assert!(!self.exhausted); + let next = self.iter.next_back(); + self.exhausted = next.is_none(); + next + } +} + +pub struct Toggle { + is_empty: bool, +} + +impl Iterator for Toggle { + type Item = (); + + // alternates between `None` and `Some(())` + fn next(&mut self) -> Option { + if self.is_empty { + self.is_empty = false; + None + } else { + self.is_empty = true; + Some(()) + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.is_empty { (0, Some(0)) } else { (1, Some(1)) } + } +} + +impl DoubleEndedIterator for Toggle { + fn next_back(&mut self) -> Option { + self.next() + } +} + +/// This is an iterator that follows the Iterator contract, +/// but it is not fused. After having returned None once, it will start +/// producing elements if .next() is called again. +pub struct CycleIter<'a, T> { + index: usize, + data: &'a [T], +} + +impl<'a, T> CycleIter<'a, T> { + pub fn new(data: &'a [T]) -> Self { + Self { index: 0, data } + } +} + +impl<'a, T> Iterator for CycleIter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option { + let elt = self.data.get(self.index); + self.index += 1; + self.index %= 1 + self.data.len(); + elt + } +} + +#[derive(Debug)] +struct CountClone(Cell); + +impl CountClone { + pub fn new() -> Self { + Self(Cell::new(0)) + } +} + +impl PartialEq for CountClone { + fn eq(&self, rhs: &i32) -> bool { + self.0.get() == *rhs + } +} + +impl Clone for CountClone { + fn clone(&self) -> Self { + let ret = CountClone(self.0.clone()); + let n = self.0.get(); + self.0.set(n + 1); + ret + } +} diff --git a/library/core/tests/iter/adapters/peekable.rs b/library/core/tests/iter/adapters/peekable.rs new file mode 100644 index 0000000000..390414d4aa --- /dev/null +++ b/library/core/tests/iter/adapters/peekable.rs @@ -0,0 +1,272 @@ +use super::*; +use core::iter::*; + +#[test] +fn test_iterator_peekable() { + let xs = vec![0, 1, 2, 3, 4, 5]; + + let mut it = xs.iter().cloned().peekable(); + assert_eq!(it.len(), 6); + assert_eq!(it.peek().unwrap(), &0); + assert_eq!(it.len(), 6); + assert_eq!(it.next().unwrap(), 0); + assert_eq!(it.len(), 5); + assert_eq!(it.next().unwrap(), 1); + assert_eq!(it.len(), 4); + assert_eq!(it.next().unwrap(), 2); + assert_eq!(it.len(), 3); + assert_eq!(it.peek().unwrap(), &3); + assert_eq!(it.len(), 3); + assert_eq!(it.peek().unwrap(), &3); + assert_eq!(it.len(), 3); + assert_eq!(it.next().unwrap(), 3); + assert_eq!(it.len(), 2); + assert_eq!(it.next().unwrap(), 4); + assert_eq!(it.len(), 1); + assert_eq!(it.peek().unwrap(), &5); + assert_eq!(it.len(), 1); + assert_eq!(it.next().unwrap(), 5); + assert_eq!(it.len(), 0); + assert!(it.peek().is_none()); + assert_eq!(it.len(), 0); + assert!(it.next().is_none()); + assert_eq!(it.len(), 0); + + let mut it = xs.iter().cloned().peekable(); + assert_eq!(it.len(), 6); + assert_eq!(it.peek().unwrap(), &0); + assert_eq!(it.len(), 6); + assert_eq!(it.next_back().unwrap(), 5); + assert_eq!(it.len(), 5); + assert_eq!(it.next_back().unwrap(), 4); + assert_eq!(it.len(), 4); + assert_eq!(it.next_back().unwrap(), 3); + assert_eq!(it.len(), 3); + assert_eq!(it.peek().unwrap(), &0); + assert_eq!(it.len(), 3); + assert_eq!(it.peek().unwrap(), &0); + assert_eq!(it.len(), 3); + assert_eq!(it.next_back().unwrap(), 2); + assert_eq!(it.len(), 2); + assert_eq!(it.next_back().unwrap(), 1); + assert_eq!(it.len(), 1); + assert_eq!(it.peek().unwrap(), &0); + assert_eq!(it.len(), 1); + assert_eq!(it.next_back().unwrap(), 0); + assert_eq!(it.len(), 0); + assert!(it.peek().is_none()); + assert_eq!(it.len(), 0); + assert!(it.next_back().is_none()); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_iterator_peekable_count() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [10]; + let zs: [i32; 0] = []; + + assert_eq!(xs.iter().peekable().count(), 6); + + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + assert_eq!(it.count(), 6); + + assert_eq!(ys.iter().peekable().count(), 1); + + let mut it = ys.iter().peekable(); + assert_eq!(it.peek(), Some(&&10)); + assert_eq!(it.count(), 1); + + assert_eq!(zs.iter().peekable().count(), 0); + + let mut it = zs.iter().peekable(); + assert_eq!(it.peek(), None); +} + +#[test] +fn test_iterator_peekable_nth() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().peekable(); + + assert_eq!(it.peek(), Some(&&0)); + assert_eq!(it.nth(0), Some(&0)); + assert_eq!(it.peek(), Some(&&1)); + assert_eq!(it.nth(1), Some(&2)); + assert_eq!(it.peek(), Some(&&3)); + assert_eq!(it.nth(2), Some(&5)); + assert_eq!(it.next(), None); +} + +#[test] +fn test_iterator_peekable_last() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [0]; + + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + assert_eq!(it.last(), Some(&5)); + + let mut it = ys.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + assert_eq!(it.last(), Some(&0)); + + let mut it = ys.iter().peekable(); + assert_eq!(it.next(), Some(&0)); + assert_eq!(it.peek(), None); + assert_eq!(it.last(), None); +} + +#[test] +fn test_iterator_peekable_fold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + let i = it.fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + +#[test] +fn test_iterator_peekable_rfold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + let i = it.rfold(0, |i, &x| { + assert_eq!(x, xs[xs.len() - 1 - i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + +#[test] +fn test_iterator_peekable_next_if_eq() { + // first, try on references + let xs = vec!["Heart", "of", "Gold"]; + let mut it = xs.into_iter().peekable(); + // try before `peek()` + assert_eq!(it.next_if_eq(&"trillian"), None); + assert_eq!(it.next_if_eq(&"Heart"), Some("Heart")); + // try after peek() + assert_eq!(it.peek(), Some(&"of")); + assert_eq!(it.next_if_eq(&"of"), Some("of")); + assert_eq!(it.next_if_eq(&"zaphod"), None); + // make sure `next()` still behaves + assert_eq!(it.next(), Some("Gold")); + + // make sure comparison works for owned values + let xs = vec![String::from("Ludicrous"), "speed".into()]; + let mut it = xs.into_iter().peekable(); + // make sure basic functionality works + assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into())); + assert_eq!(it.next_if_eq("speed"), Some("speed".into())); + assert_eq!(it.next_if_eq(""), None); +} + +#[test] +fn test_iterator_peekable_mut() { + let mut it = vec![1, 2, 3].into_iter().peekable(); + if let Some(p) = it.peek_mut() { + if *p == 1 { + *p = 5; + } + } + assert_eq!(it.collect::>(), vec![5, 2, 3]); +} + +#[test] +fn test_iterator_peekable_remember_peek_none_1() { + // Check that the loop using .peek() terminates + let data = [1, 2, 3]; + let mut iter = CycleIter::new(&data).peekable(); + + let mut n = 0; + while let Some(_) = iter.next() { + let is_the_last = iter.peek().is_none(); + assert_eq!(is_the_last, n == data.len() - 1); + n += 1; + if n > data.len() { + break; + } + } + assert_eq!(n, data.len()); +} + +#[test] +fn test_iterator_peekable_remember_peek_none_2() { + let data = [0]; + let mut iter = CycleIter::new(&data).peekable(); + iter.next(); + assert_eq!(iter.peek(), None); + assert_eq!(iter.last(), None); +} + +#[test] +fn test_iterator_peekable_remember_peek_none_3() { + let data = [0]; + let mut iter = CycleIter::new(&data).peekable(); + iter.peek(); + assert_eq!(iter.nth(0), Some(&0)); + + let mut iter = CycleIter::new(&data).peekable(); + iter.next(); + assert_eq!(iter.peek(), None); + assert_eq!(iter.nth(0), None); +} + +#[test] +fn test_peek_try_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + + assert_eq!((1..20).peekable().try_fold(7, f), (1..20).try_fold(7, f)); + assert_eq!((1..20).peekable().try_rfold(7, f), (1..20).try_rfold(7, f)); + + let mut iter = (1..20).peekable(); + assert_eq!(iter.peek(), Some(&1)); + assert_eq!(iter.try_fold(7, f), (1..20).try_fold(7, f)); + + let mut iter = (1..20).peekable(); + assert_eq!(iter.peek(), Some(&1)); + assert_eq!(iter.try_rfold(7, f), (1..20).try_rfold(7, f)); + + let mut iter = [100, 20, 30, 40, 50, 60, 70].iter().cloned().peekable(); + assert_eq!(iter.peek(), Some(&100)); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.peek(), Some(&40)); + + let mut iter = [100, 20, 30, 40, 50, 60, 70].iter().cloned().peekable(); + assert_eq!(iter.peek(), Some(&100)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.peek(), Some(&100)); + assert_eq!(iter.next_back(), Some(50)); + + let mut iter = (2..5).peekable(); + assert_eq!(iter.peek(), Some(&2)); + assert_eq!(iter.try_for_each(Err), Err(2)); + assert_eq!(iter.peek(), Some(&3)); + assert_eq!(iter.try_for_each(Err), Err(3)); + assert_eq!(iter.peek(), Some(&4)); + assert_eq!(iter.try_for_each(Err), Err(4)); + assert_eq!(iter.peek(), None); + assert_eq!(iter.try_for_each(Err), Ok(())); + + let mut iter = (2..5).peekable(); + assert_eq!(iter.peek(), Some(&2)); + assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(4)); + assert_eq!(iter.peek(), Some(&2)); + assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(3)); + assert_eq!(iter.peek(), Some(&2)); + assert_eq!(iter.try_rfold((), |(), x| Err(x)), Err(2)); + assert_eq!(iter.peek(), None); + assert_eq!(iter.try_rfold((), |(), x| Err(x)), Ok(())); +} + +#[test] +fn test_peekable_non_fused() { + let mut iter = NonFused::new(empty::()).peekable(); + + assert_eq!(iter.peek(), None); + assert_eq!(iter.next_back(), None); +} diff --git a/library/core/tests/iter/adapters/scan.rs b/library/core/tests/iter/adapters/scan.rs new file mode 100644 index 0000000000..1d28ca6b7f --- /dev/null +++ b/library/core/tests/iter/adapters/scan.rs @@ -0,0 +1,20 @@ +use core::iter::*; + +#[test] +fn test_iterator_scan() { + // test the type inference + fn add(old: &mut isize, new: &usize) -> Option { + *old += *new as isize; + Some(*old as f64) + } + let xs = [0, 1, 2, 3, 4]; + let ys = [0f64, 1.0, 3.0, 6.0, 10.0]; + + let it = xs.iter().scan(0, add); + let mut i = 0; + for x in it { + assert_eq!(x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} diff --git a/library/core/tests/iter/adapters/skip.rs b/library/core/tests/iter/adapters/skip.rs new file mode 100644 index 0000000000..cf60057a16 --- /dev/null +++ b/library/core/tests/iter/adapters/skip.rs @@ -0,0 +1,181 @@ +use core::iter::*; + +#[test] +fn test_iterator_skip() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + let ys = [13, 15, 16, 17, 19, 20, 30]; + let mut it = xs.iter().skip(5); + let mut i = 0; + while let Some(&x) = it.next() { + assert_eq!(x, ys[i]); + i += 1; + assert_eq!(it.len(), xs.len() - 5 - i); + } + assert_eq!(i, ys.len()); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_iterator_skip_doubleended() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + let mut it = xs.iter().rev().skip(5); + assert_eq!(it.next(), Some(&15)); + assert_eq!(it.by_ref().rev().next(), Some(&0)); + assert_eq!(it.next(), Some(&13)); + assert_eq!(it.by_ref().rev().next(), Some(&1)); + assert_eq!(it.next(), Some(&5)); + assert_eq!(it.by_ref().rev().next(), Some(&2)); + assert_eq!(it.next(), Some(&3)); + assert_eq!(it.next(), None); + let mut it = xs.iter().rev().skip(5).rev(); + assert_eq!(it.next(), Some(&0)); + assert_eq!(it.rev().next(), Some(&15)); + let mut it_base = xs.iter(); + { + let mut it = it_base.by_ref().skip(5).rev(); + assert_eq!(it.next(), Some(&30)); + assert_eq!(it.next(), Some(&20)); + assert_eq!(it.next(), Some(&19)); + assert_eq!(it.next(), Some(&17)); + assert_eq!(it.next(), Some(&16)); + assert_eq!(it.next(), Some(&15)); + assert_eq!(it.next(), Some(&13)); + assert_eq!(it.next(), None); + } + // make sure the skipped parts have not been consumed + assert_eq!(it_base.next(), Some(&0)); + assert_eq!(it_base.next(), Some(&1)); + assert_eq!(it_base.next(), Some(&2)); + assert_eq!(it_base.next(), Some(&3)); + assert_eq!(it_base.next(), Some(&5)); + assert_eq!(it_base.next(), None); + let it = xs.iter().skip(5).rev(); + assert_eq!(it.last(), Some(&13)); +} + +#[test] +fn test_iterator_skip_nth() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + + let mut it = xs.iter().skip(0); + assert_eq!(it.nth(0), Some(&0)); + assert_eq!(it.nth(1), Some(&2)); + + let mut it = xs.iter().skip(5); + assert_eq!(it.nth(0), Some(&13)); + assert_eq!(it.nth(1), Some(&16)); + + let mut it = xs.iter().skip(12); + assert_eq!(it.nth(0), None); +} + +#[test] +fn test_iterator_skip_count() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + + assert_eq!(xs.iter().skip(0).count(), 12); + assert_eq!(xs.iter().skip(1).count(), 11); + assert_eq!(xs.iter().skip(11).count(), 1); + assert_eq!(xs.iter().skip(12).count(), 0); + assert_eq!(xs.iter().skip(13).count(), 0); +} + +#[test] +fn test_iterator_skip_last() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + + assert_eq!(xs.iter().skip(0).last(), Some(&30)); + assert_eq!(xs.iter().skip(1).last(), Some(&30)); + assert_eq!(xs.iter().skip(11).last(), Some(&30)); + assert_eq!(xs.iter().skip(12).last(), None); + assert_eq!(xs.iter().skip(13).last(), None); + + let mut it = xs.iter().skip(5); + assert_eq!(it.next(), Some(&13)); + assert_eq!(it.last(), Some(&30)); +} + +#[test] +fn test_iterator_skip_fold() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + let ys = [13, 15, 16, 17, 19, 20, 30]; + + let it = xs.iter().skip(5); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().skip(5); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.fold(1, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().skip(5); + let i = it.rfold(ys.len(), |i, &x| { + let i = i - 1; + assert_eq!(x, ys[i]); + i + }); + assert_eq!(i, 0); + + let mut it = xs.iter().skip(5); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.rfold(ys.len(), |i, &x| { + let i = i - 1; + assert_eq!(x, ys[i]); + i + }); + assert_eq!(i, 1); +} + +#[test] +fn test_skip_try_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((1..20).skip(9).try_fold(7, f), (10..20).try_fold(7, f)); + assert_eq!((1..20).skip(9).try_rfold(7, f), (10..20).try_rfold(7, f)); + + let mut iter = (0..30).skip(10); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(24)); +} + +#[test] +fn test_skip_nth_back() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().skip(2); + assert_eq!(it.nth_back(0), Some(&5)); + assert_eq!(it.nth_back(1), Some(&3)); + assert_eq!(it.nth_back(0), Some(&2)); + assert_eq!(it.nth_back(0), None); + + let ys = [2, 3, 4, 5]; + let mut ity = ys.iter(); + let mut it = xs.iter().skip(2); + assert_eq!(it.nth_back(1), ity.nth_back(1)); + assert_eq!(it.clone().nth(0), ity.clone().nth(0)); + assert_eq!(it.nth_back(0), ity.nth_back(0)); + assert_eq!(it.clone().nth(0), ity.clone().nth(0)); + assert_eq!(it.nth_back(0), ity.nth_back(0)); + assert_eq!(it.clone().nth(0), ity.clone().nth(0)); + assert_eq!(it.nth_back(0), ity.nth_back(0)); + assert_eq!(it.clone().nth(0), ity.clone().nth(0)); + + let mut it = xs.iter().skip(2); + assert_eq!(it.nth_back(4), None); + assert_eq!(it.nth_back(0), None); + + let mut it = xs.iter(); + it.by_ref().skip(2).nth_back(3); + assert_eq!(it.next_back(), Some(&1)); + + let mut it = xs.iter(); + it.by_ref().skip(2).nth_back(10); + assert_eq!(it.next_back(), Some(&1)); +} diff --git a/library/core/tests/iter/adapters/skip_while.rs b/library/core/tests/iter/adapters/skip_while.rs new file mode 100644 index 0000000000..929d4f6e64 --- /dev/null +++ b/library/core/tests/iter/adapters/skip_while.rs @@ -0,0 +1,50 @@ +use core::iter::*; + +#[test] +fn test_iterator_skip_while() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [15, 16, 17, 19]; + let it = xs.iter().skip_while(|&x| *x < 15); + let mut i = 0; + for x in it { + assert_eq!(*x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +#[test] +fn test_iterator_skip_while_fold() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [15, 16, 17, 19]; + let it = xs.iter().skip_while(|&x| *x < 15); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().skip_while(|&x| *x < 15); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.fold(1, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); +} + +#[test] +fn test_skip_while_try_fold() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + fn p(&x: &i32) -> bool { + (x % 10) <= 5 + } + assert_eq!((1..20).skip_while(p).try_fold(7, f), (6..20).try_fold(7, f)); + let mut iter = (1..20).skip_while(p); + assert_eq!(iter.nth(5), Some(11)); + assert_eq!(iter.try_fold(7, f), (12..20).try_fold(7, f)); + + let mut iter = (0..50).skip_while(|&x| (x % 20) < 15); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(23)); +} diff --git a/library/core/tests/iter/adapters/step_by.rs b/library/core/tests/iter/adapters/step_by.rs new file mode 100644 index 0000000000..6502c7fb79 --- /dev/null +++ b/library/core/tests/iter/adapters/step_by.rs @@ -0,0 +1,248 @@ +use core::iter::*; + +#[test] +fn test_iterator_step_by() { + // Identity + let mut it = (0..).step_by(1).take(3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next(), Some(1)); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.next(), None); + + let mut it = (0..).step_by(3).take(4); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next(), Some(3)); + assert_eq!(it.next(), Some(6)); + assert_eq!(it.next(), Some(9)); + assert_eq!(it.next(), None); + + let mut it = (0..3).step_by(1); + assert_eq!(it.next_back(), Some(2)); + assert_eq!(it.next_back(), Some(1)); + assert_eq!(it.next_back(), Some(0)); + assert_eq!(it.next_back(), None); + + let mut it = (0..11).step_by(3); + assert_eq!(it.next_back(), Some(9)); + assert_eq!(it.next_back(), Some(6)); + assert_eq!(it.next_back(), Some(3)); + assert_eq!(it.next_back(), Some(0)); + assert_eq!(it.next_back(), None); +} + +#[test] +fn test_iterator_step_by_nth() { + let mut it = (0..16).step_by(5); + assert_eq!(it.nth(0), Some(0)); + assert_eq!(it.nth(0), Some(5)); + assert_eq!(it.nth(0), Some(10)); + assert_eq!(it.nth(0), Some(15)); + assert_eq!(it.nth(0), None); + + let it = (0..18).step_by(5); + assert_eq!(it.clone().nth(0), Some(0)); + assert_eq!(it.clone().nth(1), Some(5)); + assert_eq!(it.clone().nth(2), Some(10)); + assert_eq!(it.clone().nth(3), Some(15)); + assert_eq!(it.clone().nth(4), None); + assert_eq!(it.clone().nth(42), None); +} + +#[test] +fn test_iterator_step_by_nth_overflow() { + #[cfg(target_pointer_width = "8")] + type Bigger = u16; + #[cfg(target_pointer_width = "16")] + type Bigger = u32; + #[cfg(target_pointer_width = "32")] + type Bigger = u64; + #[cfg(target_pointer_width = "64")] + type Bigger = u128; + + #[derive(Clone)] + struct Test(Bigger); + impl Iterator for &mut Test { + type Item = i32; + fn next(&mut self) -> Option { + Some(21) + } + fn nth(&mut self, n: usize) -> Option { + self.0 += n as Bigger + 1; + Some(42) + } + } + + let mut it = Test(0); + let root = usize::MAX >> (usize::BITS / 2); + let n = root + 20; + (&mut it).step_by(n).nth(n); + assert_eq!(it.0, n as Bigger * n as Bigger); + + // large step + let mut it = Test(0); + (&mut it).step_by(usize::MAX).nth(5); + assert_eq!(it.0, (usize::MAX as Bigger) * 5); + + // n + 1 overflows + let mut it = Test(0); + (&mut it).step_by(2).nth(usize::MAX); + assert_eq!(it.0, (usize::MAX as Bigger) * 2); + + // n + 1 overflows + let mut it = Test(0); + (&mut it).step_by(1).nth(usize::MAX); + assert_eq!(it.0, (usize::MAX as Bigger) * 1); +} + +#[test] +fn test_iterator_step_by_nth_try_fold() { + let mut it = (0..).step_by(10); + assert_eq!(it.try_fold(0, i8::checked_add), None); + assert_eq!(it.next(), Some(60)); + assert_eq!(it.try_fold(0, i8::checked_add), None); + assert_eq!(it.next(), Some(90)); + + let mut it = (100..).step_by(10); + assert_eq!(it.try_fold(50, i8::checked_add), None); + assert_eq!(it.next(), Some(110)); + + let mut it = (100..=100).step_by(10); + assert_eq!(it.next(), Some(100)); + assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); +} + +#[test] +fn test_iterator_step_by_nth_back() { + let mut it = (0..16).step_by(5); + assert_eq!(it.nth_back(0), Some(15)); + assert_eq!(it.nth_back(0), Some(10)); + assert_eq!(it.nth_back(0), Some(5)); + assert_eq!(it.nth_back(0), Some(0)); + assert_eq!(it.nth_back(0), None); + + let mut it = (0..16).step_by(5); + assert_eq!(it.next(), Some(0)); // to set `first_take` to `false` + assert_eq!(it.nth_back(0), Some(15)); + assert_eq!(it.nth_back(0), Some(10)); + assert_eq!(it.nth_back(0), Some(5)); + assert_eq!(it.nth_back(0), None); + + let it = || (0..18).step_by(5); + assert_eq!(it().nth_back(0), Some(15)); + assert_eq!(it().nth_back(1), Some(10)); + assert_eq!(it().nth_back(2), Some(5)); + assert_eq!(it().nth_back(3), Some(0)); + assert_eq!(it().nth_back(4), None); + assert_eq!(it().nth_back(42), None); +} + +#[test] +fn test_iterator_step_by_nth_try_rfold() { + let mut it = (0..100).step_by(10); + assert_eq!(it.try_rfold(0, i8::checked_add), None); + assert_eq!(it.next_back(), Some(70)); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.try_rfold(0, i8::checked_add), None); + assert_eq!(it.next_back(), Some(30)); + + let mut it = (0..100).step_by(10); + assert_eq!(it.try_rfold(50, i8::checked_add), None); + assert_eq!(it.next_back(), Some(80)); + + let mut it = (100..=100).step_by(10); + assert_eq!(it.next_back(), Some(100)); + assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); +} + +#[test] +#[should_panic] +fn test_iterator_step_by_zero() { + let mut it = (0..).step_by(0); + it.next(); +} + +#[test] +fn test_iterator_step_by_size_hint() { + struct StubSizeHint(usize, Option); + impl Iterator for StubSizeHint { + type Item = (); + fn next(&mut self) -> Option<()> { + self.0 -= 1; + if let Some(ref mut upper) = self.1 { + *upper -= 1; + } + Some(()) + } + fn size_hint(&self) -> (usize, Option) { + (self.0, self.1) + } + } + + // The two checks in each case are needed because the logic + // is different before the first call to `next()`. + + let mut it = StubSizeHint(10, Some(10)).step_by(1); + assert_eq!(it.size_hint(), (10, Some(10))); + it.next(); + assert_eq!(it.size_hint(), (9, Some(9))); + + // exact multiple + let mut it = StubSizeHint(10, Some(10)).step_by(3); + assert_eq!(it.size_hint(), (4, Some(4))); + it.next(); + assert_eq!(it.size_hint(), (3, Some(3))); + + // larger base range, but not enough to get another element + let mut it = StubSizeHint(12, Some(12)).step_by(3); + assert_eq!(it.size_hint(), (4, Some(4))); + it.next(); + assert_eq!(it.size_hint(), (3, Some(3))); + + // smaller base range, so fewer resulting elements + let mut it = StubSizeHint(9, Some(9)).step_by(3); + assert_eq!(it.size_hint(), (3, Some(3))); + it.next(); + assert_eq!(it.size_hint(), (2, Some(2))); + + // infinite upper bound + let mut it = StubSizeHint(usize::MAX, None).step_by(1); + assert_eq!(it.size_hint(), (usize::MAX, None)); + it.next(); + assert_eq!(it.size_hint(), (usize::MAX - 1, None)); + + // still infinite with larger step + let mut it = StubSizeHint(7, None).step_by(3); + assert_eq!(it.size_hint(), (3, None)); + it.next(); + assert_eq!(it.size_hint(), (2, None)); + + // propagates ExactSizeIterator + let a = [1, 2, 3, 4, 5]; + let it = a.iter().step_by(2); + assert_eq!(it.len(), 3); + + // Cannot be TrustedLen as a step greater than one makes an iterator + // with (usize::MAX, None) no longer meet the safety requirements + trait TrustedLenCheck { + fn test(self) -> bool; + } + impl TrustedLenCheck for T { + default fn test(self) -> bool { + false + } + } + impl TrustedLenCheck for T { + fn test(self) -> bool { + true + } + } + assert!(TrustedLenCheck::test(a.iter())); + assert!(!TrustedLenCheck::test(a.iter().step_by(1))); +} + +#[test] +fn test_step_by_skip() { + assert_eq!((0..640).step_by(128).skip(1).collect::>(), [128, 256, 384, 512]); + assert_eq!((0..=50).step_by(10).nth(3), Some(30)); + assert_eq!((200..=255u8).step_by(10).nth(3), Some(230)); +} diff --git a/library/core/tests/iter/adapters/take.rs b/library/core/tests/iter/adapters/take.rs new file mode 100644 index 0000000000..89f9cb1e2e --- /dev/null +++ b/library/core/tests/iter/adapters/take.rs @@ -0,0 +1,126 @@ +use core::iter::*; + +#[test] +fn test_iterator_take() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [0, 1, 2, 3, 5]; + + let mut it = xs.iter().take(ys.len()); + let mut i = 0; + assert_eq!(it.len(), ys.len()); + while let Some(&x) = it.next() { + assert_eq!(x, ys[i]); + i += 1; + assert_eq!(it.len(), ys.len() - i); + } + assert_eq!(i, ys.len()); + assert_eq!(it.len(), 0); + + let mut it = xs.iter().take(ys.len()); + let mut i = 0; + assert_eq!(it.len(), ys.len()); + while let Some(&x) = it.next_back() { + i += 1; + assert_eq!(x, ys[ys.len() - i]); + assert_eq!(it.len(), ys.len() - i); + } + assert_eq!(i, ys.len()); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_iterator_take_nth() { + let xs = [0, 1, 2, 4, 5]; + let mut it = xs.iter(); + { + let mut take = it.by_ref().take(3); + let mut i = 0; + while let Some(&x) = take.nth(0) { + assert_eq!(x, i); + i += 1; + } + } + assert_eq!(it.nth(1), Some(&5)); + assert_eq!(it.nth(0), None); + + let xs = [0, 1, 2, 3, 4]; + let mut it = xs.iter().take(7); + let mut i = 1; + while let Some(&x) = it.nth(1) { + assert_eq!(x, i); + i += 2; + } +} + +#[test] +fn test_iterator_take_nth_back() { + let xs = [0, 1, 2, 4, 5]; + let mut it = xs.iter(); + { + let mut take = it.by_ref().take(3); + let mut i = 0; + while let Some(&x) = take.nth_back(0) { + i += 1; + assert_eq!(x, 3 - i); + } + } + assert_eq!(it.nth_back(0), None); + + let xs = [0, 1, 2, 3, 4]; + let mut it = xs.iter().take(7); + assert_eq!(it.nth_back(1), Some(&3)); + assert_eq!(it.nth_back(1), Some(&1)); + assert_eq!(it.nth_back(1), None); +} + +#[test] +fn test_iterator_take_short() { + let xs = [0, 1, 2, 3]; + + let mut it = xs.iter().take(5); + let mut i = 0; + assert_eq!(it.len(), xs.len()); + while let Some(&x) = it.next() { + assert_eq!(x, xs[i]); + i += 1; + assert_eq!(it.len(), xs.len() - i); + } + assert_eq!(i, xs.len()); + assert_eq!(it.len(), 0); + + let mut it = xs.iter().take(5); + let mut i = 0; + assert_eq!(it.len(), xs.len()); + while let Some(&x) = it.next_back() { + i += 1; + assert_eq!(x, xs[xs.len() - i]); + assert_eq!(it.len(), xs.len() - i); + } + assert_eq!(i, xs.len()); + assert_eq!(it.len(), 0); +} + +#[test] +fn test_take_try_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((10..30).take(10).try_fold(7, f), (10..20).try_fold(7, f)); + assert_eq!((10..30).take(10).try_rfold(7, f), (10..20).try_rfold(7, f)); + + let mut iter = (10..30).take(20); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); + assert_eq!(iter.try_rfold(0, i8::checked_add), None); + assert_eq!(iter.next_back(), Some(24)); + + let mut iter = (2..20).take(3); + assert_eq!(iter.try_for_each(Err), Err(2)); + assert_eq!(iter.try_for_each(Err), Err(3)); + assert_eq!(iter.try_for_each(Err), Err(4)); + assert_eq!(iter.try_for_each(Err), Ok(())); + + let mut iter = (2..20).take(3).rev(); + assert_eq!(iter.try_for_each(Err), Err(4)); + assert_eq!(iter.try_for_each(Err), Err(3)); + assert_eq!(iter.try_for_each(Err), Err(2)); + assert_eq!(iter.try_for_each(Err), Ok(())); +} diff --git a/library/core/tests/iter/adapters/take_while.rs b/library/core/tests/iter/adapters/take_while.rs new file mode 100644 index 0000000000..6f1ebab29b --- /dev/null +++ b/library/core/tests/iter/adapters/take_while.rs @@ -0,0 +1,29 @@ +use core::iter::*; + +#[test] +fn test_iterator_take_while() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [0, 1, 2, 3, 5, 13]; + let it = xs.iter().take_while(|&x| *x < 15); + let mut i = 0; + for x in it { + assert_eq!(*x, ys[i]); + i += 1; + } + assert_eq!(i, ys.len()); +} + +#[test] +fn test_take_while_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((1..20).take_while(|&x| x != 10).try_fold(7, f), (1..10).try_fold(7, f)); + let mut iter = (1..20).take_while(|&x| x != 10); + assert_eq!(iter.try_fold(0, |x, y| Some(x + y)), Some((1..10).sum())); + assert_eq!(iter.next(), None, "flag should be set"); + let iter = (1..20).take_while(|&x| x != 10); + assert_eq!(iter.fold(0, |x, y| x + y), (1..10).sum()); + + let mut iter = (10..50).take_while(|&x| x != 40); + assert_eq!(iter.try_fold(0, i8::checked_add), None); + assert_eq!(iter.next(), Some(20)); +} diff --git a/library/core/tests/iter/adapters/zip.rs b/library/core/tests/iter/adapters/zip.rs new file mode 100644 index 0000000000..1fce0951e3 --- /dev/null +++ b/library/core/tests/iter/adapters/zip.rs @@ -0,0 +1,247 @@ +use super::*; +use core::iter::*; + +#[test] +fn test_zip_nth() { + let xs = [0, 1, 2, 4, 5]; + let ys = [10, 11, 12]; + + let mut it = xs.iter().zip(&ys); + assert_eq!(it.nth(0), Some((&0, &10))); + assert_eq!(it.nth(1), Some((&2, &12))); + assert_eq!(it.nth(0), None); + + let mut it = xs.iter().zip(&ys); + assert_eq!(it.nth(3), None); + + let mut it = ys.iter().zip(&xs); + assert_eq!(it.nth(3), None); +} + +#[test] +fn test_zip_nth_side_effects() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let value = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })) + .skip(1) + .nth(3); + assert_eq!(value, Some((50, 6000))); + assert_eq!(a, vec![1, 2, 3, 4, 5]); + assert_eq!(b, vec![200, 300, 400, 500, 600]); +} + +#[test] +fn test_zip_next_back_side_effects() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let mut iter = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })); + + // The second iterator is one item longer, so `next_back` is called on it + // one more time. + assert_eq!(iter.next_back(), Some((60, 7000))); + assert_eq!(iter.next_back(), Some((50, 6000))); + assert_eq!(iter.next_back(), Some((40, 5000))); + assert_eq!(iter.next_back(), Some((30, 4000))); + assert_eq!(a, vec![6, 5, 4, 3]); + assert_eq!(b, vec![800, 700, 600, 500, 400]); +} + +#[test] +fn test_zip_nth_back_side_effects() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let value = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })) + .nth_back(3); + assert_eq!(value, Some((30, 4000))); + assert_eq!(a, vec![6, 5, 4, 3]); + assert_eq!(b, vec![800, 700, 600, 500, 400]); +} + +#[test] +fn test_zip_next_back_side_effects_exhausted() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let mut iter = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })); + + iter.next(); + iter.next(); + iter.next(); + iter.next(); + assert_eq!(iter.next_back(), None); + assert_eq!(a, vec![1, 2, 3, 4, 6, 5]); + assert_eq!(b, vec![200, 300, 400]); +} + +#[test] +fn test_zip_cloned_sideffectful() { + let xs = [CountClone::new(), CountClone::new(), CountClone::new(), CountClone::new()]; + let ys = [CountClone::new(), CountClone::new()]; + + for _ in xs.iter().cloned().zip(ys.iter().cloned()) {} + + assert_eq!(&xs, &[1, 1, 1, 0][..]); + assert_eq!(&ys, &[1, 1][..]); + + let xs = [CountClone::new(), CountClone::new()]; + let ys = [CountClone::new(), CountClone::new(), CountClone::new(), CountClone::new()]; + + for _ in xs.iter().cloned().zip(ys.iter().cloned()) {} + + assert_eq!(&xs, &[1, 1][..]); + assert_eq!(&ys, &[1, 1, 0, 0][..]); +} + +#[test] +fn test_zip_map_sideffectful() { + let mut xs = [0; 6]; + let mut ys = [0; 4]; + + for _ in xs.iter_mut().map(|x| *x += 1).zip(ys.iter_mut().map(|y| *y += 1)) {} + + assert_eq!(&xs, &[1, 1, 1, 1, 1, 0]); + assert_eq!(&ys, &[1, 1, 1, 1]); + + let mut xs = [0; 4]; + let mut ys = [0; 6]; + + for _ in xs.iter_mut().map(|x| *x += 1).zip(ys.iter_mut().map(|y| *y += 1)) {} + + assert_eq!(&xs, &[1, 1, 1, 1]); + assert_eq!(&ys, &[1, 1, 1, 1, 0, 0]); +} + +#[test] +fn test_zip_map_rev_sideffectful() { + let mut xs = [0; 6]; + let mut ys = [0; 4]; + + { + let mut it = xs.iter_mut().map(|x| *x += 1).zip(ys.iter_mut().map(|y| *y += 1)); + it.next_back(); + } + assert_eq!(&xs, &[0, 0, 0, 1, 1, 1]); + assert_eq!(&ys, &[0, 0, 0, 1]); + + let mut xs = [0; 6]; + let mut ys = [0; 4]; + + { + let mut it = xs.iter_mut().map(|x| *x += 1).zip(ys.iter_mut().map(|y| *y += 1)); + (&mut it).take(5).count(); + it.next_back(); + } + assert_eq!(&xs, &[1, 1, 1, 1, 1, 1]); + assert_eq!(&ys, &[1, 1, 1, 1]); +} + +#[test] +fn test_zip_nested_sideffectful() { + let mut xs = [0; 6]; + let ys = [0; 4]; + + { + // test that it has the side effect nested inside enumerate + let it = xs.iter_mut().map(|x| *x = 1).enumerate().zip(&ys); + it.count(); + } + assert_eq!(&xs, &[1, 1, 1, 1, 1, 0]); +} + +#[test] +fn test_zip_nth_back_side_effects_exhausted() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let mut iter = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })); + + iter.next(); + iter.next(); + iter.next(); + iter.next(); + assert_eq!(iter.nth_back(0), None); + assert_eq!(a, vec![1, 2, 3, 4, 6, 5]); + assert_eq!(b, vec![200, 300, 400]); +} + +#[test] +fn test_zip_trusted_random_access_composition() { + let a = [0, 1, 2, 3, 4]; + let b = a; + let c = a; + + let a = a.iter().copied(); + let b = b.iter().copied(); + let mut c = c.iter().copied(); + c.next(); + + let mut z1 = a.zip(b); + assert_eq!(z1.next().unwrap(), (0, 0)); + + let mut z2 = z1.zip(c); + fn assert_trusted_random_access(_a: &T) {} + assert_trusted_random_access(&z2); + assert_eq!(z2.next().unwrap(), ((1, 1), 1)); +} + +#[test] +fn test_double_ended_zip() { + let xs = [1, 2, 3, 4, 5, 6]; + let ys = [1, 2, 3, 7]; + let a = xs.iter().cloned(); + let b = ys.iter().cloned(); + let mut it = a.zip(b); + assert_eq!(it.next(), Some((1, 1))); + assert_eq!(it.next(), Some((2, 2))); + assert_eq!(it.next_back(), Some((4, 7))); + assert_eq!(it.next_back(), Some((3, 3))); + assert_eq!(it.next(), None); +} diff --git a/library/core/tests/iter/mod.rs b/library/core/tests/iter/mod.rs new file mode 100644 index 0000000000..770b6f7601 --- /dev/null +++ b/library/core/tests/iter/mod.rs @@ -0,0 +1,102 @@ +//! Note +//! ---- +//! You're probably viewing this file because you're adding a test (or you might +//! just be browsing, in that case, hey there!). +//! +//! The iter test suite is split into two big modules, and some miscellaneous +//! smaller modules. The two big modules are `adapters` and `traits`. +//! +//! `adapters` are for methods on `Iterator` that adapt the data inside the +//! iterator, whether it be by emitting another iterator or returning an item +//! from inside the iterator after executing a closure on each item. +//! +//! `traits` are for trait's that extend an `Iterator` (and the `Iterator` +//! trait itself, mostly containing miscellaneous methods). For the most part, +//! if a test in `traits` uses a specific adapter, then it should be moved to +//! that adapter's test file in `adapters`. + +mod adapters; +mod range; +mod sources; +mod traits; + +use core::cell::Cell; +use core::convert::TryFrom; +use core::iter::*; + +pub fn is_trusted_len(_: I) {} + +#[test] +fn test_multi_iter() { + let xs = [1, 2, 3, 4]; + let ys = [4, 3, 2, 1]; + assert!(xs.iter().eq(ys.iter().rev())); + assert!(xs.iter().lt(xs.iter().skip(2))); +} + +#[test] +fn test_counter_from_iter() { + let it = (0..).step_by(5).take(10); + let xs: Vec = FromIterator::from_iter(it); + assert_eq!(xs, [0, 5, 10, 15, 20, 25, 30, 35, 40, 45]); +} + +#[test] +fn test_functor_laws() { + // identity: + fn identity(x: T) -> T { + x + } + assert_eq!((0..10).map(identity).sum::(), (0..10).sum()); + + // composition: + fn f(x: usize) -> usize { + x + 3 + } + fn g(x: usize) -> usize { + x * 2 + } + fn h(x: usize) -> usize { + g(f(x)) + } + assert_eq!((0..10).map(f).map(g).sum::(), (0..10).map(h).sum()); +} + +#[test] +fn test_monad_laws_left_identity() { + fn f(x: usize) -> impl Iterator { + (0..10).map(move |y| x * y) + } + assert_eq!(once(42).flat_map(f.clone()).sum::(), f(42).sum()); +} + +#[test] +fn test_monad_laws_right_identity() { + assert_eq!((0..10).flat_map(|x| once(x)).sum::(), (0..10).sum()); +} + +#[test] +fn test_monad_laws_associativity() { + fn f(x: usize) -> impl Iterator { + 0..x + } + fn g(x: usize) -> impl Iterator { + (0..x).rev() + } + assert_eq!( + (0..10).flat_map(f).flat_map(g).sum::(), + (0..10).flat_map(|x| f(x).flat_map(g)).sum::() + ); +} + +#[test] +pub fn extend_for_unit() { + let mut x = 0; + { + let iter = (0..5).map(|_| { + x += 1; + }); + ().extend(iter); + } + assert_eq!(x, 5); +} diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs new file mode 100644 index 0000000000..44adc3c58d --- /dev/null +++ b/library/core/tests/iter/range.rs @@ -0,0 +1,446 @@ +use super::*; + +#[test] +fn test_range() { + assert_eq!((0..5).collect::>(), [0, 1, 2, 3, 4]); + assert_eq!((-10..-1).collect::>(), [-10, -9, -8, -7, -6, -5, -4, -3, -2]); + assert_eq!((0..5).rev().collect::>(), [4, 3, 2, 1, 0]); + assert_eq!((200..-5).count(), 0); + assert_eq!((200..-5).rev().count(), 0); + assert_eq!((200..200).count(), 0); + assert_eq!((200..200).rev().count(), 0); + + assert_eq!((0..100).size_hint(), (100, Some(100))); + // this test is only meaningful when sizeof usize < sizeof u64 + assert_eq!((usize::MAX - 1..usize::MAX).size_hint(), (1, Some(1))); + assert_eq!((-10..-1).size_hint(), (9, Some(9))); + assert_eq!((-1..-10).size_hint(), (0, Some(0))); + + assert_eq!((-70..58).size_hint(), (128, Some(128))); + assert_eq!((-128..127).size_hint(), (255, Some(255))); + assert_eq!( + (-2..isize::MAX).size_hint(), + (isize::MAX as usize + 2, Some(isize::MAX as usize + 2)) + ); +} + +#[test] +fn test_char_range() { + use std::char; + // Miri is too slow + let from = if cfg!(miri) { char::from_u32(0xD800 - 10).unwrap() } else { '\0' }; + let to = if cfg!(miri) { char::from_u32(0xDFFF + 10).unwrap() } else { char::MAX }; + assert!((from..=to).eq((from as u32..=to as u32).filter_map(char::from_u32))); + assert!((from..=to).rev().eq((from as u32..=to as u32).filter_map(char::from_u32).rev())); + + assert_eq!(('\u{D7FF}'..='\u{E000}').count(), 2); + assert_eq!(('\u{D7FF}'..='\u{E000}').size_hint(), (2, Some(2))); + assert_eq!(('\u{D7FF}'..'\u{E000}').count(), 1); + assert_eq!(('\u{D7FF}'..'\u{E000}').size_hint(), (1, Some(1))); +} + +#[test] +fn test_range_exhaustion() { + let mut r = 10..10; + assert!(r.is_empty()); + assert_eq!(r.next(), None); + assert_eq!(r.next_back(), None); + assert_eq!(r, 10..10); + + let mut r = 10..12; + assert_eq!(r.next(), Some(10)); + assert_eq!(r.next(), Some(11)); + assert!(r.is_empty()); + assert_eq!(r, 12..12); + assert_eq!(r.next(), None); + + let mut r = 10..12; + assert_eq!(r.next_back(), Some(11)); + assert_eq!(r.next_back(), Some(10)); + assert!(r.is_empty()); + assert_eq!(r, 10..10); + assert_eq!(r.next_back(), None); + + let mut r = 100..10; + assert!(r.is_empty()); + assert_eq!(r.next(), None); + assert_eq!(r.next_back(), None); + assert_eq!(r, 100..10); +} + +#[test] +fn test_range_inclusive_exhaustion() { + let mut r = 10..=10; + assert_eq!(r.next(), Some(10)); + assert!(r.is_empty()); + assert_eq!(r.next(), None); + assert_eq!(r.next(), None); + + assert_eq!(*r.start(), 10); + assert_eq!(*r.end(), 10); + assert_ne!(r, 10..=10); + + let mut r = 10..=10; + assert_eq!(r.next_back(), Some(10)); + assert!(r.is_empty()); + assert_eq!(r.next_back(), None); + + assert_eq!(*r.start(), 10); + assert_eq!(*r.end(), 10); + assert_ne!(r, 10..=10); + + let mut r = 10..=12; + assert_eq!(r.next(), Some(10)); + assert_eq!(r.next(), Some(11)); + assert_eq!(r.next(), Some(12)); + assert!(r.is_empty()); + assert_eq!(r.next(), None); + + let mut r = 10..=12; + assert_eq!(r.next_back(), Some(12)); + assert_eq!(r.next_back(), Some(11)); + assert_eq!(r.next_back(), Some(10)); + assert!(r.is_empty()); + assert_eq!(r.next_back(), None); + + let mut r = 10..=12; + assert_eq!(r.nth(2), Some(12)); + assert!(r.is_empty()); + assert_eq!(r.next(), None); + + let mut r = 10..=12; + assert_eq!(r.nth(5), None); + assert!(r.is_empty()); + assert_eq!(r.next(), None); + + let mut r = 100..=10; + assert_eq!(r.next(), None); + assert!(r.is_empty()); + assert_eq!(r.next(), None); + assert_eq!(r.next(), None); + assert_eq!(r, 100..=10); + + let mut r = 100..=10; + assert_eq!(r.next_back(), None); + assert!(r.is_empty()); + assert_eq!(r.next_back(), None); + assert_eq!(r.next_back(), None); + assert_eq!(r, 100..=10); +} + +#[test] +fn test_range_nth() { + assert_eq!((10..15).nth(0), Some(10)); + assert_eq!((10..15).nth(1), Some(11)); + assert_eq!((10..15).nth(4), Some(14)); + assert_eq!((10..15).nth(5), None); + + let mut r = 10..20; + assert_eq!(r.nth(2), Some(12)); + assert_eq!(r, 13..20); + assert_eq!(r.nth(2), Some(15)); + assert_eq!(r, 16..20); + assert_eq!(r.nth(10), None); + assert_eq!(r, 20..20); +} + +#[test] +fn test_range_nth_back() { + assert_eq!((10..15).nth_back(0), Some(14)); + assert_eq!((10..15).nth_back(1), Some(13)); + assert_eq!((10..15).nth_back(4), Some(10)); + assert_eq!((10..15).nth_back(5), None); + assert_eq!((-120..80_i8).nth_back(199), Some(-120)); + + let mut r = 10..20; + assert_eq!(r.nth_back(2), Some(17)); + assert_eq!(r, 10..17); + assert_eq!(r.nth_back(2), Some(14)); + assert_eq!(r, 10..14); + assert_eq!(r.nth_back(10), None); + assert_eq!(r, 10..10); +} + +#[test] +fn test_range_from_nth() { + assert_eq!((10..).nth(0), Some(10)); + assert_eq!((10..).nth(1), Some(11)); + assert_eq!((10..).nth(4), Some(14)); + + let mut r = 10..; + assert_eq!(r.nth(2), Some(12)); + assert_eq!(r, 13..); + assert_eq!(r.nth(2), Some(15)); + assert_eq!(r, 16..); + assert_eq!(r.nth(10), Some(26)); + assert_eq!(r, 27..); + + assert_eq!((0..).size_hint(), (usize::MAX, None)); +} + +#[test] +fn test_range_from_take() { + let mut it = (0..).take(3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next(), Some(1)); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.next(), None); + is_trusted_len((0..).take(3)); + assert_eq!((0..).take(3).size_hint(), (3, Some(3))); + assert_eq!((0..).take(0).size_hint(), (0, Some(0))); + assert_eq!((0..).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); +} + +#[test] +fn test_range_from_take_collect() { + let v: Vec<_> = (0..).take(3).collect(); + assert_eq!(v, vec![0, 1, 2]); +} + +#[test] +fn test_range_inclusive_nth() { + assert_eq!((10..=15).nth(0), Some(10)); + assert_eq!((10..=15).nth(1), Some(11)); + assert_eq!((10..=15).nth(5), Some(15)); + assert_eq!((10..=15).nth(6), None); + + let mut exhausted_via_next = 10_u8..=20; + while exhausted_via_next.next().is_some() {} + + let mut r = 10_u8..=20; + assert_eq!(r.nth(2), Some(12)); + assert_eq!(r, 13..=20); + assert_eq!(r.nth(2), Some(15)); + assert_eq!(r, 16..=20); + assert_eq!(r.is_empty(), false); + assert_eq!(ExactSizeIterator::is_empty(&r), false); + assert_eq!(r.nth(10), None); + assert_eq!(r.is_empty(), true); + assert_eq!(r, exhausted_via_next); + assert_eq!(ExactSizeIterator::is_empty(&r), true); +} + +#[test] +fn test_range_inclusive_nth_back() { + assert_eq!((10..=15).nth_back(0), Some(15)); + assert_eq!((10..=15).nth_back(1), Some(14)); + assert_eq!((10..=15).nth_back(5), Some(10)); + assert_eq!((10..=15).nth_back(6), None); + assert_eq!((-120..=80_i8).nth_back(200), Some(-120)); + + let mut exhausted_via_next_back = 10_u8..=20; + while exhausted_via_next_back.next_back().is_some() {} + + let mut r = 10_u8..=20; + assert_eq!(r.nth_back(2), Some(18)); + assert_eq!(r, 10..=17); + assert_eq!(r.nth_back(2), Some(15)); + assert_eq!(r, 10..=14); + assert_eq!(r.is_empty(), false); + assert_eq!(ExactSizeIterator::is_empty(&r), false); + assert_eq!(r.nth_back(10), None); + assert_eq!(r.is_empty(), true); + assert_eq!(r, exhausted_via_next_back); + assert_eq!(ExactSizeIterator::is_empty(&r), true); +} + +#[test] +fn test_range_len() { + assert_eq!((0..10_u8).len(), 10); + assert_eq!((9..10_u8).len(), 1); + assert_eq!((10..10_u8).len(), 0); + assert_eq!((11..10_u8).len(), 0); + assert_eq!((100..10_u8).len(), 0); +} + +#[test] +fn test_range_inclusive_len() { + assert_eq!((0..=10_u8).len(), 11); + assert_eq!((9..=10_u8).len(), 2); + assert_eq!((10..=10_u8).len(), 1); + assert_eq!((11..=10_u8).len(), 0); + assert_eq!((100..=10_u8).len(), 0); +} + +#[test] +fn test_range_step() { + #![allow(deprecated)] + + assert_eq!((0..20).step_by(5).collect::>(), [0, 5, 10, 15]); + assert_eq!((1..21).rev().step_by(5).collect::>(), [20, 15, 10, 5]); + assert_eq!((1..21).rev().step_by(6).collect::>(), [20, 14, 8, 2]); + assert_eq!((200..255).step_by(50).collect::>(), [200, 250]); + assert_eq!((200..-5).step_by(1).collect::>(), []); + assert_eq!((200..200).step_by(1).collect::>(), []); + + assert_eq!((0..20).step_by(1).size_hint(), (20, Some(20))); + assert_eq!((0..20).step_by(21).size_hint(), (1, Some(1))); + assert_eq!((0..20).step_by(5).size_hint(), (4, Some(4))); + assert_eq!((1..21).rev().step_by(5).size_hint(), (4, Some(4))); + assert_eq!((1..21).rev().step_by(6).size_hint(), (4, Some(4))); + assert_eq!((20..-5).step_by(1).size_hint(), (0, Some(0))); + assert_eq!((20..20).step_by(1).size_hint(), (0, Some(0))); + assert_eq!((i8::MIN..i8::MAX).step_by(-(i8::MIN as i32) as usize).size_hint(), (2, Some(2))); + assert_eq!((i16::MIN..i16::MAX).step_by(i16::MAX as usize).size_hint(), (3, Some(3))); + assert_eq!((isize::MIN..isize::MAX).step_by(1).size_hint(), (usize::MAX, Some(usize::MAX))); +} + +#[test] +fn test_range_inclusive_step() { + assert_eq!((0..=50).step_by(10).collect::>(), [0, 10, 20, 30, 40, 50]); + assert_eq!((0..=5).step_by(1).collect::>(), [0, 1, 2, 3, 4, 5]); + assert_eq!((200..=255u8).step_by(10).collect::>(), [200, 210, 220, 230, 240, 250]); + assert_eq!((250..=255u8).step_by(1).collect::>(), [250, 251, 252, 253, 254, 255]); +} + +#[test] +fn test_range_last_max() { + assert_eq!((0..20).last(), Some(19)); + assert_eq!((-20..0).last(), Some(-1)); + assert_eq!((5..5).last(), None); + + assert_eq!((0..20).max(), Some(19)); + assert_eq!((-20..0).max(), Some(-1)); + assert_eq!((5..5).max(), None); +} + +#[test] +fn test_range_inclusive_last_max() { + assert_eq!((0..=20).last(), Some(20)); + assert_eq!((-20..=0).last(), Some(0)); + assert_eq!((5..=5).last(), Some(5)); + let mut r = 10..=10; + r.next(); + assert_eq!(r.last(), None); + + assert_eq!((0..=20).max(), Some(20)); + assert_eq!((-20..=0).max(), Some(0)); + assert_eq!((5..=5).max(), Some(5)); + let mut r = 10..=10; + r.next(); + assert_eq!(r.max(), None); +} + +#[test] +fn test_range_min() { + assert_eq!((0..20).min(), Some(0)); + assert_eq!((-20..0).min(), Some(-20)); + assert_eq!((5..5).min(), None); +} + +#[test] +fn test_range_inclusive_min() { + assert_eq!((0..=20).min(), Some(0)); + assert_eq!((-20..=0).min(), Some(-20)); + assert_eq!((5..=5).min(), Some(5)); + let mut r = 10..=10; + r.next(); + assert_eq!(r.min(), None); +} + +#[test] +fn test_range_inclusive_folds() { + assert_eq!((1..=10).sum::(), 55); + assert_eq!((1..=10).rev().sum::(), 55); + + let mut it = 44..=50; + assert_eq!(it.try_fold(0, i8::checked_add), None); + assert_eq!(it, 47..=50); + assert_eq!(it.try_fold(0, i8::checked_add), None); + assert_eq!(it, 50..=50); + assert_eq!(it.try_fold(0, i8::checked_add), Some(50)); + assert!(it.is_empty()); + assert_eq!(it.try_fold(0, i8::checked_add), Some(0)); + assert!(it.is_empty()); + + let mut it = 40..=47; + assert_eq!(it.try_rfold(0, i8::checked_add), None); + assert_eq!(it, 40..=44); + assert_eq!(it.try_rfold(0, i8::checked_add), None); + assert_eq!(it, 40..=41); + assert_eq!(it.try_rfold(0, i8::checked_add), Some(81)); + assert!(it.is_empty()); + assert_eq!(it.try_rfold(0, i8::checked_add), Some(0)); + assert!(it.is_empty()); + + let mut it = 10..=20; + assert_eq!(it.try_fold(0, |a, b| Some(a + b)), Some(165)); + assert!(it.is_empty()); + assert_eq!(it.try_fold(0, |a, b| Some(a + b)), Some(0)); + assert!(it.is_empty()); + + let mut it = 10..=20; + assert_eq!(it.try_rfold(0, |a, b| Some(a + b)), Some(165)); + assert!(it.is_empty()); + assert_eq!(it.try_rfold(0, |a, b| Some(a + b)), Some(0)); + assert!(it.is_empty()); +} + +#[test] +fn test_range_size_hint() { + assert_eq!((0..0usize).size_hint(), (0, Some(0))); + assert_eq!((0..100usize).size_hint(), (100, Some(100))); + assert_eq!((0..usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); + + let umax = u128::try_from(usize::MAX).unwrap(); + assert_eq!((0..0u128).size_hint(), (0, Some(0))); + assert_eq!((0..100u128).size_hint(), (100, Some(100))); + assert_eq!((0..umax).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((0..umax + 1).size_hint(), (usize::MAX, None)); + + assert_eq!((0..0isize).size_hint(), (0, Some(0))); + assert_eq!((-100..100isize).size_hint(), (200, Some(200))); + assert_eq!((isize::MIN..isize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); + + let imin = i128::try_from(isize::MIN).unwrap(); + let imax = i128::try_from(isize::MAX).unwrap(); + assert_eq!((0..0i128).size_hint(), (0, Some(0))); + assert_eq!((-100..100i128).size_hint(), (200, Some(200))); + assert_eq!((imin..imax).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((imin..imax + 1).size_hint(), (usize::MAX, None)); +} + +#[test] +fn test_range_inclusive_size_hint() { + assert_eq!((1..=0usize).size_hint(), (0, Some(0))); + assert_eq!((0..=0usize).size_hint(), (1, Some(1))); + assert_eq!((0..=100usize).size_hint(), (101, Some(101))); + assert_eq!((0..=usize::MAX - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((0..=usize::MAX).size_hint(), (usize::MAX, None)); + + let umax = u128::try_from(usize::MAX).unwrap(); + assert_eq!((1..=0u128).size_hint(), (0, Some(0))); + assert_eq!((0..=0u128).size_hint(), (1, Some(1))); + assert_eq!((0..=100u128).size_hint(), (101, Some(101))); + assert_eq!((0..=umax - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((0..=umax).size_hint(), (usize::MAX, None)); + assert_eq!((0..=umax + 1).size_hint(), (usize::MAX, None)); + + assert_eq!((0..=-1isize).size_hint(), (0, Some(0))); + assert_eq!((0..=0isize).size_hint(), (1, Some(1))); + assert_eq!((-100..=100isize).size_hint(), (201, Some(201))); + assert_eq!((isize::MIN..=isize::MAX - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((isize::MIN..=isize::MAX).size_hint(), (usize::MAX, None)); + + let imin = i128::try_from(isize::MIN).unwrap(); + let imax = i128::try_from(isize::MAX).unwrap(); + assert_eq!((0..=-1i128).size_hint(), (0, Some(0))); + assert_eq!((0..=0i128).size_hint(), (1, Some(1))); + assert_eq!((-100..=100i128).size_hint(), (201, Some(201))); + assert_eq!((imin..=imax - 1).size_hint(), (usize::MAX, Some(usize::MAX))); + assert_eq!((imin..=imax).size_hint(), (usize::MAX, None)); + assert_eq!((imin..=imax + 1).size_hint(), (usize::MAX, None)); +} + +#[test] +fn test_double_ended_range() { + assert_eq!((11..14).rev().collect::>(), [13, 12, 11]); + for _ in (10..0).rev() { + panic!("unreachable"); + } + + assert_eq!((11..14).rev().collect::>(), [13, 12, 11]); + for _ in (10..0).rev() { + panic!("unreachable"); + } +} diff --git a/library/core/tests/iter/sources.rs b/library/core/tests/iter/sources.rs new file mode 100644 index 0000000000..d0114ade6e --- /dev/null +++ b/library/core/tests/iter/sources.rs @@ -0,0 +1,108 @@ +use super::*; +use core::iter::*; + +#[test] +fn test_repeat() { + let mut it = repeat(42); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(repeat(42).size_hint(), (usize::MAX, None)); +} + +#[test] +fn test_repeat_take() { + let mut it = repeat(42).take(3); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), None); + is_trusted_len(repeat(42).take(3)); + assert_eq!(repeat(42).take(3).size_hint(), (3, Some(3))); + assert_eq!(repeat(42).take(0).size_hint(), (0, Some(0))); + assert_eq!(repeat(42).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); +} + +#[test] +fn test_repeat_take_collect() { + let v: Vec<_> = repeat(42).take(3).collect(); + assert_eq!(v, vec![42, 42, 42]); +} + +#[test] +fn test_repeat_with() { + #[derive(PartialEq, Debug)] + struct NotClone(usize); + let mut it = repeat_with(|| NotClone(42)); + assert_eq!(it.next(), Some(NotClone(42))); + assert_eq!(it.next(), Some(NotClone(42))); + assert_eq!(it.next(), Some(NotClone(42))); + assert_eq!(repeat_with(|| NotClone(42)).size_hint(), (usize::MAX, None)); +} + +#[test] +fn test_repeat_with_take() { + let mut it = repeat_with(|| 42).take(3); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), None); + is_trusted_len(repeat_with(|| 42).take(3)); + assert_eq!(repeat_with(|| 42).take(3).size_hint(), (3, Some(3))); + assert_eq!(repeat_with(|| 42).take(0).size_hint(), (0, Some(0))); + assert_eq!(repeat_with(|| 42).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX))); +} + +#[test] +fn test_repeat_with_take_collect() { + let mut curr = 1; + let v: Vec<_> = repeat_with(|| { + let tmp = curr; + curr *= 2; + tmp + }) + .take(5) + .collect(); + assert_eq!(v, vec![1, 2, 4, 8, 16]); +} + +#[test] +fn test_successors() { + let mut powers_of_10 = successors(Some(1_u16), |n| n.checked_mul(10)); + assert_eq!(powers_of_10.by_ref().collect::>(), &[1, 10, 100, 1_000, 10_000]); + assert_eq!(powers_of_10.next(), None); + + let mut empty = successors(None::, |_| unimplemented!()); + assert_eq!(empty.next(), None); + assert_eq!(empty.next(), None); +} + +#[test] +fn test_once() { + let mut it = once(42); + assert_eq!(it.next(), Some(42)); + assert_eq!(it.next(), None); +} + +#[test] +fn test_once_with() { + let count = Cell::new(0); + let mut it = once_with(|| { + count.set(count.get() + 1); + 42 + }); + + assert_eq!(count.get(), 0); + assert_eq!(it.next(), Some(42)); + assert_eq!(count.get(), 1); + assert_eq!(it.next(), None); + assert_eq!(count.get(), 1); + assert_eq!(it.next(), None); + assert_eq!(count.get(), 1); +} + +#[test] +fn test_empty() { + let mut it = empty::(); + assert_eq!(it.next(), None); +} diff --git a/library/core/tests/iter/traits/accum.rs b/library/core/tests/iter/traits/accum.rs new file mode 100644 index 0000000000..f3eeb31fe5 --- /dev/null +++ b/library/core/tests/iter/traits/accum.rs @@ -0,0 +1,66 @@ +use core::iter::*; + +#[test] +fn test_iterator_sum() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..4].iter().cloned().sum::(), 6); + assert_eq!(v.iter().cloned().sum::(), 55); + assert_eq!(v[..0].iter().cloned().sum::(), 0); +} + +#[test] +fn test_iterator_sum_result() { + let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().sum::>(), Ok(10)); + let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().sum::>(), Err(())); + + #[derive(PartialEq, Debug)] + struct S(Result); + + impl Sum> for S { + fn sum>>(mut iter: I) -> Self { + // takes the sum by repeatedly calling `next` on `iter`, + // thus testing that repeated calls to `ResultShunt::try_fold` + // produce the expected results + Self(iter.by_ref().sum()) + } + } + + let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().sum::(), S(Ok(10))); + let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().sum::(), S(Err(()))); +} + +#[test] +fn test_iterator_sum_option() { + let v: &[Option] = &[Some(1), Some(2), Some(3), Some(4)]; + assert_eq!(v.iter().cloned().sum::>(), Some(10)); + let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; + assert_eq!(v.iter().cloned().sum::>(), None); +} + +#[test] +fn test_iterator_product() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..4].iter().cloned().product::(), 0); + assert_eq!(v[1..5].iter().cloned().product::(), 24); + assert_eq!(v[..0].iter().cloned().product::(), 1); +} + +#[test] +fn test_iterator_product_result() { + let v: &[Result] = &[Ok(1), Ok(2), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().product::>(), Ok(24)); + let v: &[Result] = &[Ok(1), Err(()), Ok(3), Ok(4)]; + assert_eq!(v.iter().cloned().product::>(), Err(())); +} + +#[test] +fn test_iterator_product_option() { + let v: &[Option] = &[Some(1), Some(2), Some(3), Some(4)]; + assert_eq!(v.iter().cloned().product::>(), Some(24)); + let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; + assert_eq!(v.iter().cloned().product::>(), None); +} diff --git a/library/core/tests/iter/traits/double_ended.rs b/library/core/tests/iter/traits/double_ended.rs new file mode 100644 index 0000000000..947d19d3df --- /dev/null +++ b/library/core/tests/iter/traits/double_ended.rs @@ -0,0 +1,90 @@ +//! Note +//! ---- +//! You're probably viewing this file because you're adding a test (or you might +//! just be browsing, in that case, hey there!). +//! +//! If you've made a test that happens to use one of DoubleEnded's methods, but +//! it tests another adapter or trait, you should *add it to the adapter or +//! trait's test file*. +//! +//! Some examples would be `adapters::cloned::test_cloned_try_folds` or +//! `adapters::flat_map::test_double_ended_flat_map`, which use `try_fold` and +//! `next_back`, but test their own adapter. + +#[test] +fn test_iterator_rev_nth_back() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().rev().nth_back(i).unwrap(), &v[i]); + } + assert_eq!(v.iter().rev().nth_back(v.len()), None); +} + +#[test] +fn test_iterator_rev_nth() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().rev().nth(i).unwrap(), &v[v.len() - 1 - i]); + } + assert_eq!(v.iter().rev().nth(v.len()), None); +} + +#[test] +fn test_rev() { + let xs = [2, 4, 6, 8, 10, 12, 14, 16]; + let mut it = xs.iter(); + it.next(); + it.next(); + assert!(it.rev().cloned().collect::>() == vec![16, 14, 12, 10, 8, 6]); +} + +#[test] +fn test_rev_try_folds() { + let f = &|acc, x| i32::checked_add(2 * acc, x); + assert_eq!((1..10).rev().try_fold(7, f), (1..10).try_rfold(7, f)); + assert_eq!((1..10).rev().try_rfold(7, f), (1..10).try_fold(7, f)); + + let a = [10, 20, 30, 40, 100, 60, 70, 80, 90]; + let mut iter = a.iter().rev(); + assert_eq!(iter.try_fold(0_i8, |acc, &x| acc.checked_add(x)), None); + assert_eq!(iter.next(), Some(&70)); + let mut iter = a.iter().rev(); + assert_eq!(iter.try_rfold(0_i8, |acc, &x| acc.checked_add(x)), None); + assert_eq!(iter.next_back(), Some(&60)); +} + +#[test] +fn test_rposition() { + fn f(xy: &(isize, char)) -> bool { + let (_x, y) = *xy; + y == 'b' + } + fn g(xy: &(isize, char)) -> bool { + let (_x, y) = *xy; + y == 'd' + } + let v = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'b')]; + + assert_eq!(v.iter().rposition(f), Some(3)); + assert!(v.iter().rposition(g).is_none()); +} + +#[test] +fn test_rev_rposition() { + let v = [0, 0, 1, 1]; + assert_eq!(v.iter().rev().rposition(|&x| x == 1), Some(1)); +} + +#[test] +#[should_panic] +fn test_rposition_panic() { + let v: [(Box<_>, Box<_>); 4] = [(box 0, box 0), (box 0, box 0), (box 0, box 0), (box 0, box 0)]; + let mut i = 0; + v.iter().rposition(|_elt| { + if i == 2 { + panic!() + } + i += 1; + false + }); +} diff --git a/library/core/tests/iter/traits/iterator.rs b/library/core/tests/iter/traits/iterator.rs new file mode 100644 index 0000000000..422e389e38 --- /dev/null +++ b/library/core/tests/iter/traits/iterator.rs @@ -0,0 +1,470 @@ +/// A wrapper struct that implements `Eq` and `Ord` based on the wrapped +/// integer modulo 3. Used to test that `Iterator::max` and `Iterator::min` +/// return the correct element if some of them are equal. +#[derive(Debug)] +struct Mod3(i32); + +impl PartialEq for Mod3 { + fn eq(&self, other: &Self) -> bool { + self.0 % 3 == other.0 % 3 + } +} + +impl Eq for Mod3 {} + +impl PartialOrd for Mod3 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Mod3 { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + (self.0 % 3).cmp(&(other.0 % 3)) + } +} + +#[test] +fn test_lt() { + let empty: [isize; 0] = []; + let xs = [1, 2, 3]; + let ys = [1, 2, 0]; + + assert!(!xs.iter().lt(ys.iter())); + assert!(!xs.iter().le(ys.iter())); + assert!(xs.iter().gt(ys.iter())); + assert!(xs.iter().ge(ys.iter())); + + assert!(ys.iter().lt(xs.iter())); + assert!(ys.iter().le(xs.iter())); + assert!(!ys.iter().gt(xs.iter())); + assert!(!ys.iter().ge(xs.iter())); + + assert!(empty.iter().lt(xs.iter())); + assert!(empty.iter().le(xs.iter())); + assert!(!empty.iter().gt(xs.iter())); + assert!(!empty.iter().ge(xs.iter())); + + // Sequence with NaN + let u = [1.0f64, 2.0]; + let v = [0.0f64 / 0.0, 3.0]; + + assert!(!u.iter().lt(v.iter())); + assert!(!u.iter().le(v.iter())); + assert!(!u.iter().gt(v.iter())); + assert!(!u.iter().ge(v.iter())); + + let a = [0.0f64 / 0.0]; + let b = [1.0f64]; + let c = [2.0f64]; + + assert!(a.iter().lt(b.iter()) == (a[0] < b[0])); + assert!(a.iter().le(b.iter()) == (a[0] <= b[0])); + assert!(a.iter().gt(b.iter()) == (a[0] > b[0])); + assert!(a.iter().ge(b.iter()) == (a[0] >= b[0])); + + assert!(c.iter().lt(b.iter()) == (c[0] < b[0])); + assert!(c.iter().le(b.iter()) == (c[0] <= b[0])); + assert!(c.iter().gt(b.iter()) == (c[0] > b[0])); + assert!(c.iter().ge(b.iter()) == (c[0] >= b[0])); +} + +#[test] +fn test_cmp_by() { + use core::cmp::Ordering; + + let f = |x: i32, y: i32| (x * x).cmp(&y); + let xs = || [1, 2, 3, 4].iter().copied(); + let ys = || [1, 4, 16].iter().copied(); + + assert_eq!(xs().cmp_by(ys(), f), Ordering::Less); + assert_eq!(ys().cmp_by(xs(), f), Ordering::Greater); + assert_eq!(xs().cmp_by(xs().map(|x| x * x), f), Ordering::Equal); + assert_eq!(xs().rev().cmp_by(ys().rev(), f), Ordering::Greater); + assert_eq!(xs().cmp_by(ys().rev(), f), Ordering::Less); + assert_eq!(xs().cmp_by(ys().take(2), f), Ordering::Greater); +} + +#[test] +fn test_partial_cmp_by() { + use core::cmp::Ordering; + + let f = |x: i32, y: i32| (x * x).partial_cmp(&y); + let xs = || [1, 2, 3, 4].iter().copied(); + let ys = || [1, 4, 16].iter().copied(); + + assert_eq!(xs().partial_cmp_by(ys(), f), Some(Ordering::Less)); + assert_eq!(ys().partial_cmp_by(xs(), f), Some(Ordering::Greater)); + assert_eq!(xs().partial_cmp_by(xs().map(|x| x * x), f), Some(Ordering::Equal)); + assert_eq!(xs().rev().partial_cmp_by(ys().rev(), f), Some(Ordering::Greater)); + assert_eq!(xs().partial_cmp_by(xs().rev(), f), Some(Ordering::Less)); + assert_eq!(xs().partial_cmp_by(ys().take(2), f), Some(Ordering::Greater)); + + let f = |x: f64, y: f64| (x * x).partial_cmp(&y); + let xs = || [1.0, 2.0, 3.0, 4.0].iter().copied(); + let ys = || [1.0, 4.0, f64::NAN, 16.0].iter().copied(); + + assert_eq!(xs().partial_cmp_by(ys(), f), None); + assert_eq!(ys().partial_cmp_by(xs(), f), Some(Ordering::Greater)); +} + +#[test] +fn test_eq_by() { + let f = |x: i32, y: i32| x * x == y; + let xs = || [1, 2, 3, 4].iter().copied(); + let ys = || [1, 4, 9, 16].iter().copied(); + + assert!(xs().eq_by(ys(), f)); + assert!(!ys().eq_by(xs(), f)); + assert!(!xs().eq_by(xs(), f)); + assert!(!ys().eq_by(ys(), f)); + + assert!(!xs().take(3).eq_by(ys(), f)); + assert!(!xs().eq_by(ys().take(3), f)); + assert!(xs().take(3).eq_by(ys().take(3), f)); +} + +#[test] +fn test_iterator_nth() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().nth(i).unwrap(), &v[i]); + } + assert_eq!(v.iter().nth(v.len()), None); +} + +#[test] +fn test_iterator_nth_back() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().nth_back(i).unwrap(), &v[v.len() - 1 - i]); + } + assert_eq!(v.iter().nth_back(v.len()), None); +} + +#[test] +fn test_iterator_advance_by() { + let v: &[_] = &[0, 1, 2, 3, 4]; + + for i in 0..v.len() { + let mut iter = v.iter(); + assert_eq!(iter.advance_by(i), Ok(())); + assert_eq!(iter.next().unwrap(), &v[i]); + assert_eq!(iter.advance_by(100), Err(v.len() - 1 - i)); + } + + assert_eq!(v.iter().advance_by(v.len()), Ok(())); + assert_eq!(v.iter().advance_by(100), Err(v.len())); +} + +#[test] +fn test_iterator_advance_back_by() { + let v: &[_] = &[0, 1, 2, 3, 4]; + + for i in 0..v.len() { + let mut iter = v.iter(); + assert_eq!(iter.advance_back_by(i), Ok(())); + assert_eq!(iter.next_back().unwrap(), &v[v.len() - 1 - i]); + assert_eq!(iter.advance_back_by(100), Err(v.len() - 1 - i)); + } + + assert_eq!(v.iter().advance_back_by(v.len()), Ok(())); + assert_eq!(v.iter().advance_back_by(100), Err(v.len())); +} + +#[test] +fn test_iterator_rev_advance_back_by() { + let v: &[_] = &[0, 1, 2, 3, 4]; + + for i in 0..v.len() { + let mut iter = v.iter().rev(); + assert_eq!(iter.advance_back_by(i), Ok(())); + assert_eq!(iter.next_back().unwrap(), &v[i]); + assert_eq!(iter.advance_back_by(100), Err(v.len() - 1 - i)); + } + + assert_eq!(v.iter().rev().advance_back_by(v.len()), Ok(())); + assert_eq!(v.iter().rev().advance_back_by(100), Err(v.len())); +} + +#[test] +fn test_iterator_last() { + let v: &[_] = &[0, 1, 2, 3, 4]; + assert_eq!(v.iter().last().unwrap(), &4); + assert_eq!(v[..1].iter().last().unwrap(), &0); +} + +#[test] +fn test_iterator_max() { + let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..4].iter().cloned().max(), Some(3)); + assert_eq!(v.iter().cloned().max(), Some(10)); + assert_eq!(v[..0].iter().cloned().max(), None); + assert_eq!(v.iter().cloned().map(Mod3).max().map(|x| x.0), Some(8)); +} + +#[test] +fn test_iterator_min() { + let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..4].iter().cloned().min(), Some(0)); + assert_eq!(v.iter().cloned().min(), Some(0)); + assert_eq!(v[..0].iter().cloned().min(), None); + assert_eq!(v.iter().cloned().map(Mod3).min().map(|x| x.0), Some(0)); +} + +#[test] +fn test_iterator_size_hint() { + let c = (0..).step_by(1); + let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let v2 = &[10, 11, 12]; + let vi = v.iter(); + + assert_eq!((0..).size_hint(), (usize::MAX, None)); + assert_eq!(c.size_hint(), (usize::MAX, None)); + assert_eq!(vi.clone().size_hint(), (10, Some(10))); + + assert_eq!(c.clone().take(5).size_hint(), (5, Some(5))); + assert_eq!(c.clone().skip(5).size_hint().1, None); + assert_eq!(c.clone().take_while(|_| false).size_hint(), (0, None)); + assert_eq!(c.clone().map_while(|_| None::<()>).size_hint(), (0, None)); + assert_eq!(c.clone().skip_while(|_| false).size_hint(), (0, None)); + assert_eq!(c.clone().enumerate().size_hint(), (usize::MAX, None)); + assert_eq!(c.clone().chain(vi.clone().cloned()).size_hint(), (usize::MAX, None)); + assert_eq!(c.clone().zip(vi.clone()).size_hint(), (10, Some(10))); + assert_eq!(c.clone().scan(0, |_, _| Some(0)).size_hint(), (0, None)); + assert_eq!(c.clone().filter(|_| false).size_hint(), (0, None)); + assert_eq!(c.clone().map(|_| 0).size_hint(), (usize::MAX, None)); + assert_eq!(c.filter_map(|_| Some(0)).size_hint(), (0, None)); + + assert_eq!(vi.clone().take(5).size_hint(), (5, Some(5))); + assert_eq!(vi.clone().take(12).size_hint(), (10, Some(10))); + assert_eq!(vi.clone().skip(3).size_hint(), (7, Some(7))); + assert_eq!(vi.clone().skip(12).size_hint(), (0, Some(0))); + assert_eq!(vi.clone().take_while(|_| false).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().map_while(|_| None::<()>).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().skip_while(|_| false).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().enumerate().size_hint(), (10, Some(10))); + assert_eq!(vi.clone().chain(v2).size_hint(), (13, Some(13))); + assert_eq!(vi.clone().zip(v2).size_hint(), (3, Some(3))); + assert_eq!(vi.clone().scan(0, |_, _| Some(0)).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().filter(|_| false).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().map(|&i| i + 1).size_hint(), (10, Some(10))); + assert_eq!(vi.filter_map(|_| Some(0)).size_hint(), (0, Some(10))); +} + +#[test] +fn test_all() { + let v: Box<[isize]> = Box::new([1, 2, 3, 4, 5]); + assert!(v.iter().all(|&x| x < 10)); + assert!(!v.iter().all(|&x| x % 2 == 0)); + assert!(!v.iter().all(|&x| x > 100)); + assert!(v[..0].iter().all(|_| panic!())); +} + +#[test] +fn test_any() { + let v: Box<[isize]> = Box::new([1, 2, 3, 4, 5]); + assert!(v.iter().any(|&x| x < 10)); + assert!(v.iter().any(|&x| x % 2 == 0)); + assert!(!v.iter().any(|&x| x > 100)); + assert!(!v[..0].iter().any(|_| panic!())); +} + +#[test] +fn test_find() { + let v: &[isize] = &[1, 3, 9, 27, 103, 14, 11]; + assert_eq!(*v.iter().find(|&&x| x & 1 == 0).unwrap(), 14); + assert_eq!(*v.iter().find(|&&x| x % 3 == 0).unwrap(), 3); + assert!(v.iter().find(|&&x| x % 12 == 0).is_none()); +} + +#[test] +fn test_try_find() { + let xs: &[isize] = &[]; + assert_eq!(xs.iter().try_find(testfn), Ok(None)); + let xs: &[isize] = &[1, 2, 3, 4]; + assert_eq!(xs.iter().try_find(testfn), Ok(Some(&2))); + let xs: &[isize] = &[1, 3, 4]; + assert_eq!(xs.iter().try_find(testfn), Err(())); + + let xs: &[isize] = &[1, 2, 3, 4, 5, 6, 7]; + let mut iter = xs.iter(); + assert_eq!(iter.try_find(testfn), Ok(Some(&2))); + assert_eq!(iter.try_find(testfn), Err(())); + assert_eq!(iter.next(), Some(&5)); + + fn testfn(x: &&isize) -> Result { + if **x == 2 { + return Ok(true); + } + if **x == 4 { + return Err(()); + } + Ok(false) + } +} + +#[test] +fn test_try_find_api_usability() -> Result<(), Box> { + let a = ["1", "2"]; + + let is_my_num = |s: &str, search: i32| -> Result { + Ok(s.parse::()? == search) + }; + + let val = a.iter().try_find(|&&s| is_my_num(s, 2))?; + assert_eq!(val, Some(&"2")); + + Ok(()) +} + +#[test] +fn test_position() { + let v = &[1, 3, 9, 27, 103, 14, 11]; + assert_eq!(v.iter().position(|x| *x & 1 == 0).unwrap(), 5); + assert_eq!(v.iter().position(|x| *x % 3 == 0).unwrap(), 1); + assert!(v.iter().position(|x| *x % 12 == 0).is_none()); +} + +#[test] +fn test_count() { + let xs = &[1, 2, 2, 1, 5, 9, 0, 2]; + assert_eq!(xs.iter().filter(|x| **x == 2).count(), 3); + assert_eq!(xs.iter().filter(|x| **x == 5).count(), 1); + assert_eq!(xs.iter().filter(|x| **x == 95).count(), 0); +} + +#[test] +fn test_max_by_key() { + let xs: &[isize] = &[-3, 0, 1, 5, -10]; + assert_eq!(*xs.iter().max_by_key(|x| x.abs()).unwrap(), -10); +} + +#[test] +fn test_max_by() { + let xs: &[isize] = &[-3, 0, 1, 5, -10]; + assert_eq!(*xs.iter().max_by(|x, y| x.abs().cmp(&y.abs())).unwrap(), -10); +} + +#[test] +fn test_min_by_key() { + let xs: &[isize] = &[-3, 0, 1, 5, -10]; + assert_eq!(*xs.iter().min_by_key(|x| x.abs()).unwrap(), 0); +} + +#[test] +fn test_min_by() { + let xs: &[isize] = &[-3, 0, 1, 5, -10]; + assert_eq!(*xs.iter().min_by(|x, y| x.abs().cmp(&y.abs())).unwrap(), 0); +} + +#[test] +fn test_by_ref() { + let mut xs = 0..10; + // sum the first five values + let partial_sum = xs.by_ref().take(5).fold(0, |a, b| a + b); + assert_eq!(partial_sum, 10); + assert_eq!(xs.next(), Some(5)); +} + +#[test] +fn test_is_sorted() { + assert!([1, 2, 2, 9].iter().is_sorted()); + assert!(![1, 3, 2].iter().is_sorted()); + assert!([0].iter().is_sorted()); + assert!(std::iter::empty::().is_sorted()); + assert!(![0.0, 1.0, f32::NAN].iter().is_sorted()); + assert!([-2, -1, 0, 3].iter().is_sorted()); + assert!(![-2i32, -1, 0, 3].iter().is_sorted_by_key(|n| n.abs())); + assert!(!["c", "bb", "aaa"].iter().is_sorted()); + assert!(["c", "bb", "aaa"].iter().is_sorted_by_key(|s| s.len())); +} + +#[test] +fn test_partition() { + fn check(xs: &mut [i32], ref p: impl Fn(&i32) -> bool, expected: usize) { + let i = xs.iter_mut().partition_in_place(p); + assert_eq!(expected, i); + assert!(xs[..i].iter().all(p)); + assert!(!xs[i..].iter().any(p)); + assert!(xs.iter().is_partitioned(p)); + if i == 0 || i == xs.len() { + assert!(xs.iter().rev().is_partitioned(p)); + } else { + assert!(!xs.iter().rev().is_partitioned(p)); + } + } + + check(&mut [], |_| true, 0); + check(&mut [], |_| false, 0); + + check(&mut [0], |_| true, 1); + check(&mut [0], |_| false, 0); + + check(&mut [-1, 1], |&x| x > 0, 1); + check(&mut [-1, 1], |&x| x < 0, 1); + + let ref mut xs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + check(xs, |_| true, 10); + check(xs, |_| false, 0); + check(xs, |&x| x % 2 == 0, 5); // evens + check(xs, |&x| x % 2 == 1, 5); // odds + check(xs, |&x| x % 3 == 0, 4); // multiple of 3 + check(xs, |&x| x % 4 == 0, 3); // multiple of 4 + check(xs, |&x| x % 5 == 0, 2); // multiple of 5 + check(xs, |&x| x < 3, 3); // small + check(xs, |&x| x > 6, 3); // large +} + +#[test] +fn test_iterator_rev_advance_by() { + let v: &[_] = &[0, 1, 2, 3, 4]; + + for i in 0..v.len() { + let mut iter = v.iter().rev(); + assert_eq!(iter.advance_by(i), Ok(())); + assert_eq!(iter.next().unwrap(), &v[v.len() - 1 - i]); + assert_eq!(iter.advance_by(100), Err(v.len() - 1 - i)); + } + + assert_eq!(v.iter().rev().advance_by(v.len()), Ok(())); + assert_eq!(v.iter().rev().advance_by(100), Err(v.len())); +} + +#[test] +fn test_find_map() { + let xs: &[isize] = &[]; + assert_eq!(xs.iter().find_map(half_if_even), None); + let xs: &[isize] = &[3, 5]; + assert_eq!(xs.iter().find_map(half_if_even), None); + let xs: &[isize] = &[4, 5]; + assert_eq!(xs.iter().find_map(half_if_even), Some(2)); + let xs: &[isize] = &[3, 6]; + assert_eq!(xs.iter().find_map(half_if_even), Some(3)); + + let xs: &[isize] = &[1, 2, 3, 4, 5, 6, 7]; + let mut iter = xs.iter(); + assert_eq!(iter.find_map(half_if_even), Some(1)); + assert_eq!(iter.find_map(half_if_even), Some(2)); + assert_eq!(iter.find_map(half_if_even), Some(3)); + assert_eq!(iter.next(), Some(&7)); + + fn half_if_even(x: &isize) -> Option { + if x % 2 == 0 { Some(x / 2) } else { None } + } +} + +#[test] +fn test_iterator_len() { + let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..4].iter().count(), 4); + assert_eq!(v[..10].iter().count(), 10); + assert_eq!(v[..0].iter().count(), 0); +} + +#[test] +fn test_collect() { + let a = vec![1, 2, 3, 4, 5]; + let b: Vec = a.iter().cloned().collect(); + assert!(a == b); +} diff --git a/library/core/tests/iter/traits/mod.rs b/library/core/tests/iter/traits/mod.rs new file mode 100644 index 0000000000..80619f53f2 --- /dev/null +++ b/library/core/tests/iter/traits/mod.rs @@ -0,0 +1,4 @@ +mod accum; +mod double_ended; +mod iterator; +mod step; diff --git a/library/core/tests/iter/traits/step.rs b/library/core/tests/iter/traits/step.rs new file mode 100644 index 0000000000..3d82a40cd2 --- /dev/null +++ b/library/core/tests/iter/traits/step.rs @@ -0,0 +1,89 @@ +use core::iter::*; + +#[test] +fn test_steps_between() { + assert_eq!(Step::steps_between(&20_u8, &200_u8), Some(180_usize)); + assert_eq!(Step::steps_between(&-20_i8, &80_i8), Some(100_usize)); + assert_eq!(Step::steps_between(&-120_i8, &80_i8), Some(200_usize)); + assert_eq!(Step::steps_between(&20_u32, &4_000_100_u32), Some(4_000_080_usize)); + assert_eq!(Step::steps_between(&-20_i32, &80_i32), Some(100_usize)); + assert_eq!(Step::steps_between(&-2_000_030_i32, &2_000_050_i32), Some(4_000_080_usize)); + + // Skip u64/i64 to avoid differences with 32-bit vs 64-bit platforms + + assert_eq!(Step::steps_between(&20_u128, &200_u128), Some(180_usize)); + assert_eq!(Step::steps_between(&-20_i128, &80_i128), Some(100_usize)); + if cfg!(target_pointer_width = "64") { + assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_0009_u128), Some(usize::MAX)); + } + assert_eq!(Step::steps_between(&10_u128, &0x1_0000_0000_0000_000a_u128), None); + assert_eq!(Step::steps_between(&10_i128, &0x1_0000_0000_0000_000a_i128), None); + assert_eq!( + Step::steps_between(&-0x1_0000_0000_0000_0000_i128, &0x1_0000_0000_0000_0000_i128,), + None, + ); +} + +#[test] +fn test_step_forward() { + assert_eq!(Step::forward_checked(55_u8, 200_usize), Some(255_u8)); + assert_eq!(Step::forward_checked(252_u8, 200_usize), None); + assert_eq!(Step::forward_checked(0_u8, 256_usize), None); + assert_eq!(Step::forward_checked(-110_i8, 200_usize), Some(90_i8)); + assert_eq!(Step::forward_checked(-110_i8, 248_usize), None); + assert_eq!(Step::forward_checked(-126_i8, 256_usize), None); + + assert_eq!(Step::forward_checked(35_u16, 100_usize), Some(135_u16)); + assert_eq!(Step::forward_checked(35_u16, 65500_usize), Some(u16::MAX)); + assert_eq!(Step::forward_checked(36_u16, 65500_usize), None); + assert_eq!(Step::forward_checked(-110_i16, 200_usize), Some(90_i16)); + assert_eq!(Step::forward_checked(-20_030_i16, 50_050_usize), Some(30_020_i16)); + assert_eq!(Step::forward_checked(-10_i16, 40_000_usize), None); + assert_eq!(Step::forward_checked(-10_i16, 70_000_usize), None); + + assert_eq!(Step::forward_checked(10_u128, 70_000_usize), Some(70_010_u128)); + assert_eq!(Step::forward_checked(10_i128, 70_030_usize), Some(70_040_i128)); + assert_eq!( + Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0xff_usize), + Some(u128::MAX), + ); + assert_eq!( + Step::forward_checked(0xffff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_u128, 0x100_usize), + None + ); + assert_eq!( + Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0xff_usize), + Some(i128::MAX), + ); + assert_eq!( + Step::forward_checked(0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize), + None + ); +} + +#[test] +fn test_step_backward() { + assert_eq!(Step::backward_checked(255_u8, 200_usize), Some(55_u8)); + assert_eq!(Step::backward_checked(100_u8, 200_usize), None); + assert_eq!(Step::backward_checked(255_u8, 256_usize), None); + assert_eq!(Step::backward_checked(90_i8, 200_usize), Some(-110_i8)); + assert_eq!(Step::backward_checked(110_i8, 248_usize), None); + assert_eq!(Step::backward_checked(127_i8, 256_usize), None); + + assert_eq!(Step::backward_checked(135_u16, 100_usize), Some(35_u16)); + assert_eq!(Step::backward_checked(u16::MAX, 65500_usize), Some(35_u16)); + assert_eq!(Step::backward_checked(10_u16, 11_usize), None); + assert_eq!(Step::backward_checked(90_i16, 200_usize), Some(-110_i16)); + assert_eq!(Step::backward_checked(30_020_i16, 50_050_usize), Some(-20_030_i16)); + assert_eq!(Step::backward_checked(-10_i16, 40_000_usize), None); + assert_eq!(Step::backward_checked(-10_i16, 70_000_usize), None); + + assert_eq!(Step::backward_checked(70_010_u128, 70_000_usize), Some(10_u128)); + assert_eq!(Step::backward_checked(70_020_i128, 70_030_usize), Some(-10_i128)); + assert_eq!(Step::backward_checked(10_u128, 7_usize), Some(3_u128)); + assert_eq!(Step::backward_checked(10_u128, 11_usize), None); + assert_eq!( + Step::backward_checked(-0x7fff_ffff_ffff_ffff__ffff_ffff_ffff_ff00_i128, 0x100_usize), + Some(i128::MIN) + ); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 2828235c3e..4dc86e0f5f 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -13,6 +13,8 @@ #![feature(const_assume)] #![feature(const_cell_into_inner)] #![feature(const_maybe_uninit_assume_init)] +#![feature(const_ptr_read)] +#![feature(const_ptr_offset)] #![feature(core_intrinsics)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] @@ -34,6 +36,9 @@ #![feature(raw)] #![feature(sort_internals)] #![feature(slice_partition_at_index)] +#![feature(maybe_uninit_uninit_array)] +#![feature(maybe_uninit_array_assume_init)] +#![feature(maybe_uninit_extra)] #![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(step_trait)] @@ -45,9 +50,9 @@ #![feature(slice_internals)] #![feature(slice_partition_dedup)] #![feature(int_error_matching)] -#![feature(array_value_iter)] #![feature(iter_advance_by)] #![feature(iter_partition_in_place)] +#![feature(iter_intersperse)] #![feature(iter_is_partitioned)] #![feature(iter_order_by)] #![feature(cmp_min_max_by)] @@ -58,8 +63,8 @@ #![feature(const_raw_ptr_deref)] #![feature(never_type)] #![feature(unwrap_infallible)] +#![feature(option_result_unwrap_unchecked)] #![feature(option_unwrap_none)] -#![feature(peekable_next_if)] #![feature(peekable_peek_mut)] #![feature(partition_point)] #![feature(once_cell)] @@ -68,6 +73,8 @@ #![feature(nonzero_leading_trailing_zeros)] #![feature(const_option)] #![feature(integer_atomics)] +#![feature(slice_group_by)] +#![feature(trusted_random_access)] #![deny(unsafe_op_in_unsafe_fn)] extern crate test; @@ -82,6 +89,10 @@ mod cell; mod char; mod clone; mod cmp; + +#[cfg(not(bootstrap))] +mod const_ptr; + mod fmt; mod hash; mod intrinsics; diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index 5d0fedd4d9..38084f401b 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -134,13 +134,28 @@ fn test_discriminant_send_sync() { } #[test] -#[cfg(not(bootstrap))] fn assume_init_good() { const TRUE: bool = unsafe { MaybeUninit::::new(true).assume_init() }; assert!(TRUE); } +#[test] +fn uninit_array_assume_init() { + let mut array: [MaybeUninit; 5] = MaybeUninit::uninit_array(); + array[0].write(3); + array[1].write(1); + array[2].write(4); + array[3].write(1); + array[4].write(5); + + let array = unsafe { MaybeUninit::array_assume_init(array) }; + + assert_eq!(array, [3, 1, 4, 1, 5]); + + let [] = unsafe { MaybeUninit::::array_assume_init([]) }; +} + #[test] fn uninit_write_slice() { let mut dst = [MaybeUninit::new(255); 64]; @@ -267,3 +282,10 @@ fn uninit_write_slice_cloned_no_drop() { forget(src); } + +#[test] +#[cfg(not(bootstrap))] +fn uninit_const_assume_init_read() { + const FOO: u32 = unsafe { MaybeUninit::new(42).assume_init_read() }; + assert_eq!(FOO, 42); +} diff --git a/library/core/tests/nonzero.rs b/library/core/tests/nonzero.rs index b66c482c5e..c2c08522d0 100644 --- a/library/core/tests/nonzero.rs +++ b/library/core/tests/nonzero.rs @@ -312,3 +312,19 @@ fn nonzero_trailing_zeros() { const TRAILING_ZEROS: u32 = NonZeroU16::new(1 << 2).unwrap().trailing_zeros(); assert_eq!(TRAILING_ZEROS, 2); } + +#[test] +fn test_nonzero_uint_div() { + let nz = NonZeroU32::new(1).unwrap(); + + let x: u32 = 42u32 / nz; + assert_eq!(x, 42u32); +} + +#[test] +fn test_nonzero_uint_rem() { + let nz = NonZeroU32::new(10).unwrap(); + + let x: u32 = 42u32 % nz; + assert_eq!(x, 2u32); +} diff --git a/library/core/tests/num/dec2flt/mod.rs b/library/core/tests/num/dec2flt/mod.rs index 1c172f49c2..32f05d1def 100644 --- a/library/core/tests/num/dec2flt/mod.rs +++ b/library/core/tests/num/dec2flt/mod.rs @@ -21,7 +21,6 @@ macro_rules! test_literal { }}; } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn ordinary() { test_literal!(1.0); @@ -38,7 +37,6 @@ fn ordinary() { test_literal!(2.2250738585072014e-308); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn special_code_paths() { test_literal!(36893488147419103229.0); // 2^65 - 3, triggers half-to-even with even significand diff --git a/library/core/tests/num/dec2flt/rawfp.rs b/library/core/tests/num/dec2flt/rawfp.rs index c098b9c2ba..34a37209d9 100644 --- a/library/core/tests/num/dec2flt/rawfp.rs +++ b/library/core/tests/num/dec2flt/rawfp.rs @@ -81,7 +81,6 @@ fn rounding_overflow() { assert_eq!(rounded.k, adjusted_k + 1); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn prev_float_monotonic() { let mut x = 1.0; @@ -117,7 +116,6 @@ fn next_float_inf() { assert_eq!(next_float(f64::INFINITY), f64::INFINITY); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn next_prev_identity() { for &x in &SOME_FLOATS { @@ -128,7 +126,6 @@ fn next_prev_identity() { } } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn next_float_monotonic() { let mut x = 0.49999999999999; diff --git a/library/core/tests/num/flt2dec/strategy/dragon.rs b/library/core/tests/num/flt2dec/strategy/dragon.rs index 3d985c6796..fc2e724a20 100644 --- a/library/core/tests/num/flt2dec/strategy/dragon.rs +++ b/library/core/tests/num/flt2dec/strategy/dragon.rs @@ -13,7 +13,6 @@ fn test_mul_pow10() { } } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn shortest_sanity_test() { f64_shortest_sanity_test(format_shortest); diff --git a/library/core/tests/num/flt2dec/strategy/grisu.rs b/library/core/tests/num/flt2dec/strategy/grisu.rs index 7e6c8add33..b59a3b9b72 100644 --- a/library/core/tests/num/flt2dec/strategy/grisu.rs +++ b/library/core/tests/num/flt2dec/strategy/grisu.rs @@ -33,7 +33,6 @@ fn test_max_pow10_no_more_than() { } } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn shortest_sanity_test() { f64_shortest_sanity_test(format_shortest); diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 012ab4ea5c..e66a73ac12 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -29,6 +29,8 @@ mod u8; mod bignum; mod dec2flt; mod flt2dec; +mod ops; +mod wrapping; mod nan; diff --git a/library/core/tests/num/ops.rs b/library/core/tests/num/ops.rs new file mode 100644 index 0000000000..9979cc8fde --- /dev/null +++ b/library/core/tests/num/ops.rs @@ -0,0 +1,244 @@ +use core::ops::*; + +// For types L and R, checks that a trait implementation exists for +// * binary ops: L op R, L op &R, &L op R and &L op &R +// * assign ops: &mut L op R, &mut L op &R +macro_rules! impl_defined { + ($op:ident, $method:ident($lhs:literal, $rhs:literal), $result:literal, $lt:ty, $rt:ty) => { + let lhs = $lhs as $lt; + let rhs = $rhs as $rt; + assert_eq!($result as $lt, $op::$method(lhs, rhs)); + assert_eq!($result as $lt, $op::$method(lhs, &rhs)); + assert_eq!($result as $lt, $op::$method(&lhs, rhs)); + assert_eq!($result as $lt, $op::$method(&lhs, &rhs)); + }; + ($op:ident, $method:ident(&mut $lhs:literal, $rhs:literal), $result:literal, $lt:ty, $rt:ty) => { + let rhs = $rhs as $rt; + let mut lhs = $lhs as $lt; + $op::$method(&mut lhs, rhs); + assert_eq!($result as $lt, lhs); + + let mut lhs = $lhs as $lt; + $op::$method(&mut lhs, &rhs); + assert_eq!($result as $lt, lhs); + }; +} + +// For all specified types T, checks that a trait implementation exists for +// * binary ops: T op T, T op &T, &T op T and &T op &T +// * assign ops: &mut T op T, &mut T op &T +// * unary ops: op T and op &T +macro_rules! impls_defined { + ($op:ident, $method:ident($lhs:literal, $rhs:literal), $result:literal, $($t:ty),+) => {$( + impl_defined!($op, $method($lhs, $rhs), $result, $t, $t); + )+}; + ($op:ident, $method:ident(&mut $lhs:literal, $rhs:literal), $result:literal, $($t:ty),+) => {$( + impl_defined!($op, $method(&mut $lhs, $rhs), $result, $t, $t); + )+}; + ($op:ident, $method:ident($operand:literal), $result:literal, $($t:ty),+) => {$( + let operand = $operand as $t; + assert_eq!($result as $t, $op::$method(operand)); + assert_eq!($result as $t, $op::$method(&operand)); + )+}; +} + +macro_rules! test_op { + ($fn_name:ident, $op:ident::$method:ident($lhs:literal, $rhs:literal), $result:literal, $($t:ty),+) => { + #[test] + fn $fn_name() { + impls_defined!($op, $method($lhs, $rhs), $result, $($t),+); + } + }; + ($fn_name:ident, $op:ident::$method:ident(&mut $lhs:literal, $rhs:literal), $result:literal, $($t:ty),+) => { + #[test] + fn $fn_name() { + impls_defined!($op, $method(&mut $lhs, $rhs), $result, $($t),+); + } + }; + ($fn_name:ident, $op:ident::$method:ident($lhs:literal), $result:literal, $($t:ty),+) => { + #[test] + fn $fn_name() { + impls_defined!($op, $method($lhs), $result, $($t),+); + } + }; +} + +test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, f32, f64); +#[cfg(not(target_os = "emscripten"))] +test_op!(test_neg_defined_128, Neg::neg(0), 0, i128); + +test_op!(test_not_defined_bool, Not::not(true), false, bool); + +macro_rules! test_arith_op { + ($fn_name:ident, $op:ident::$method:ident($lhs:literal, $rhs:literal)) => { + #[test] + fn $fn_name() { + impls_defined!( + $op, + $method($lhs, $rhs), + 0, + i8, + i16, + i32, + i64, + isize, + u8, + u16, + u32, + u64, + usize, + f32, + f64 + ); + #[cfg(not(target_os = "emscripten"))] + impls_defined!($op, $method($lhs, $rhs), 0, i128, u128); + } + }; + ($fn_name:ident, $op:ident::$method:ident(&mut $lhs:literal, $rhs:literal)) => { + #[test] + fn $fn_name() { + impls_defined!( + $op, + $method(&mut $lhs, $rhs), + 0, + i8, + i16, + i32, + i64, + isize, + u8, + u16, + u32, + u64, + usize, + f32, + f64 + ); + #[cfg(not(target_os = "emscripten"))] + impls_defined!($op, $method(&mut $lhs, $rhs), 0, i128, u128); + } + }; +} + +test_arith_op!(test_add_defined, Add::add(0, 0)); +test_arith_op!(test_add_assign_defined, AddAssign::add_assign(&mut 0, 0)); +test_arith_op!(test_sub_defined, Sub::sub(0, 0)); +test_arith_op!(test_sub_assign_defined, SubAssign::sub_assign(&mut 0, 0)); +test_arith_op!(test_mul_defined, Mul::mul(0, 0)); +test_arith_op!(test_mul_assign_defined, MulAssign::mul_assign(&mut 0, 0)); +test_arith_op!(test_div_defined, Div::div(0, 1)); +test_arith_op!(test_div_assign_defined, DivAssign::div_assign(&mut 0, 1)); +test_arith_op!(test_rem_defined, Rem::rem(0, 1)); +test_arith_op!(test_rem_assign_defined, RemAssign::rem_assign(&mut 0, 1)); + +macro_rules! test_bitop { + ($test_name:ident, $op:ident::$method:ident) => { + #[test] + fn $test_name() { + impls_defined!( + $op, + $method(0, 0), + 0, + i8, + i16, + i32, + i64, + isize, + u8, + u16, + u32, + u64, + usize + ); + #[cfg(not(target_os = "emscripten"))] + impls_defined!($op, $method(0, 0), 0, i128, u128); + impls_defined!($op, $method(false, false), false, bool); + } + }; +} +macro_rules! test_bitop_assign { + ($test_name:ident, $op:ident::$method:ident) => { + #[test] + fn $test_name() { + impls_defined!( + $op, + $method(&mut 0, 0), + 0, + i8, + i16, + i32, + i64, + isize, + u8, + u16, + u32, + u64, + usize + ); + #[cfg(not(target_os = "emscripten"))] + impls_defined!($op, $method(&mut 0, 0), 0, i128, u128); + impls_defined!($op, $method(&mut false, false), false, bool); + } + }; +} + +test_bitop!(test_bitand_defined, BitAnd::bitand); +test_bitop_assign!(test_bitand_assign_defined, BitAndAssign::bitand_assign); +test_bitop!(test_bitor_defined, BitOr::bitor); +test_bitop_assign!(test_bitor_assign_defined, BitOrAssign::bitor_assign); +test_bitop!(test_bitxor_defined, BitXor::bitxor); +test_bitop_assign!(test_bitxor_assign_defined, BitXorAssign::bitxor_assign); + +macro_rules! test_shift_inner { + ($op:ident::$method:ident, $lt:ty, $($rt:ty),+) => { + $(impl_defined!($op, $method(0,0), 0, $lt, $rt);)+ + }; + ($op:ident::$method:ident, $lt:ty) => { + test_shift_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); + #[cfg(not(target_os = "emscripten"))] + test_shift_inner!($op::$method, $lt, i128, u128); + }; +} + +macro_rules! test_shift { + ($op:ident::$method:ident, $($lt:ty),+) => { + $(test_shift_inner!($op::$method, $lt);)+ + }; + ($test_name:ident, $op:ident::$method:ident) => { + #[test] + fn $test_name() { + test_shift!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); + #[cfg(not(target_os = "emscripten"))] + test_shift!($op::$method, i128, u128); + } + }; +} + +macro_rules! test_shift_assign_inner { + ($op:ident::$method:ident, $lt:ty, $($rt:ty),+) => { + $(impl_defined!($op, $method(&mut 0,0), 0, $lt, $rt);)+ + }; + ($op:ident::$method:ident, $lt:ty) => { + test_shift_assign_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); + #[cfg(not(target_os = "emscripten"))] + test_shift_assign_inner!($op::$method, $lt, i128, u128); + }; +} + +macro_rules! test_shift_assign { + ($op:ident::$method:ident, $($lt:ty),+) => { + $(test_shift_assign_inner!($op::$method, $lt);)+ + }; + ($test_name:ident, $op:ident::$method:ident) => { + #[test] + fn $test_name() { + test_shift_assign!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); + #[cfg(not(target_os = "emscripten"))] + test_shift_assign!($op::$method, i128, u128); + } + }; +} +test_shift!(test_shl_defined, Shl::shl); +test_shift_assign!(test_shl_assign_defined, ShlAssign::shl_assign); +test_shift!(test_shr_defined, Shr::shr); +test_shift_assign!(test_shr_assign_defined, ShrAssign::shr_assign); diff --git a/library/core/tests/num/wrapping.rs b/library/core/tests/num/wrapping.rs index 01defab2b3..c4fb321ef2 100644 --- a/library/core/tests/num/wrapping.rs +++ b/library/core/tests/num/wrapping.rs @@ -26,9 +26,9 @@ macro_rules! wrapping_assignment { } macro_rules! wrapping_test { - ($type:ty, $min:expr, $max:expr) => { + ($fn_name:ident, $type:ty, $min:expr, $max:expr) => { #[test] - fn wrapping_$type() { + fn $fn_name() { let zero: Wrapping<$type> = Wrapping(0); let one: Wrapping<$type> = Wrapping(1); let min: Wrapping<$type> = Wrapping($min); @@ -60,23 +60,24 @@ macro_rules! wrapping_test { }; } -wrapping_test!(i8, i8::MIN, i8::MAX); -wrapping_test!(i16, i16::MIN, i16::MAX); -wrapping_test!(i32, i32::MIN, i32::MAX); -wrapping_test!(i64, i64::MIN, i64::MAX); +wrapping_test!(test_wrapping_i8, i8, i8::MIN, i8::MAX); +wrapping_test!(test_wrapping_i16, i16, i16::MIN, i16::MAX); +wrapping_test!(test_wrapping_i32, i32, i32::MIN, i32::MAX); +wrapping_test!(test_wrapping_i64, i64, i64::MIN, i64::MAX); #[cfg(not(target_os = "emscripten"))] -wrapping_test!(i128, i128::MIN, i128::MAX); -wrapping_test!(isize, isize::MIN, isize::MAX); -wrapping_test!(u8, u8::MIN, u8::MAX); -wrapping_test!(u16, u16::MIN, u16::MAX); -wrapping_test!(u32, u32::MIN, u32::MAX); -wrapping_test!(u64, u64::MIN, u64::MAX); +wrapping_test!(test_wrapping_i128, i128, i128::MIN, i128::MAX); +wrapping_test!(test_wrapping_isize, isize, isize::MIN, isize::MAX); +wrapping_test!(test_wrapping_u8, u8, u8::MIN, u8::MAX); +wrapping_test!(test_wrapping_u16, u16, u16::MIN, u16::MAX); +wrapping_test!(test_wrapping_u32, u32, u32::MIN, u32::MAX); +wrapping_test!(test_wrapping_u64, u64, u64::MIN, u64::MAX); #[cfg(not(target_os = "emscripten"))] -wrapping_test!(u128, u128::MIN, u128::MAX); -wrapping_test!(usize, usize::MIN, usize::MAX); +wrapping_test!(test_wrapping_u128, u128, u128::MIN, u128::MAX); +wrapping_test!(test_wrapping_usize, usize, usize::MIN, usize::MAX); // Don't warn about overflowing ops on 32-bit platforms #[cfg_attr(target_pointer_width = "32", allow(const_err))] +#[test] fn wrapping_int_api() { assert_eq!(i8::MAX.wrapping_add(1), i8::MIN); assert_eq!(i16::MAX.wrapping_add(1), i16::MIN); diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index 5388b47562..9470451278 100644 --- a/library/core/tests/option.rs +++ b/library/core/tests/option.rs @@ -160,6 +160,13 @@ fn test_unwrap_or_else() { assert_eq!(x.unwrap_or_else(|| 2), 2); } +#[test] +fn test_unwrap_unchecked() { + assert_eq!(unsafe { Some(1).unwrap_unchecked() }, 1); + let s = unsafe { Some("hello".to_string()).unwrap_unchecked() }; + assert_eq!(s, "hello"); +} + #[test] fn test_iter() { let val = 5; diff --git a/library/core/tests/result.rs b/library/core/tests/result.rs index 81660870e9..7aa44c6e59 100644 --- a/library/core/tests/result.rs +++ b/library/core/tests/result.rs @@ -119,6 +119,18 @@ pub fn test_unwrap_or_else_panic() { let _: isize = bad_err.unwrap_or_else(handler); } +#[test] +fn test_unwrap_unchecked() { + let ok: Result = Ok(100); + assert_eq!(unsafe { ok.unwrap_unchecked() }, 100); +} + +#[test] +fn test_unwrap_err_unchecked() { + let ok_err: Result = Err("Err"); + assert_eq!(unsafe { ok_err.unwrap_err_unchecked() }, "Err"); +} + #[test] pub fn test_expect_ok() { let ok: Result = Ok(100); diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index a8ebb4b321..eb2277d8ba 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -14,11 +14,13 @@ #![feature(core_intrinsics)] #![feature(nll)] #![feature(panic_runtime)] +#![feature(std_internals)] #![feature(staged_api)] #![feature(rustc_attrs)] #![feature(asm)] use core::any::Any; +use core::panic::BoxMeUp; #[rustc_std_internal_symbol] #[allow(improper_ctypes_definitions)] @@ -28,7 +30,7 @@ pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Sen // "Leak" the payload and shim to the relevant abort on the platform in question. #[rustc_std_internal_symbol] -pub unsafe extern "C" fn __rust_start_panic(_payload: usize) -> u32 { +pub unsafe extern "C" fn __rust_start_panic(_payload: *mut &mut dyn BoxMeUp) -> u32 { abort(); cfg_if::cfg_if! { diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 0b74a844fe..9ce9c477ec 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -104,9 +104,8 @@ pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any // implementation. #[rustc_std_internal_symbol] #[unwind(allowed)] -pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 { - let payload = payload as *mut &mut dyn BoxMeUp; - let payload = (*payload).take_box(); +pub unsafe extern "C" fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32 { + let payload = Box::from_raw((*payload).take_box()); - imp::panic(Box::from_raw(payload)) + imp::panic(payload) } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index ca40293ed5..a89e7b53e4 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -28,8 +28,7 @@ #![feature(extern_types)] #![feature(in_band_lifetimes)] #![feature(negative_impls)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![feature(restricted_std)] #![feature(rustc_attrs)] #![feature(min_specialization)] diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index b64f13dba4..c6a4548ec0 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -14,8 +14,7 @@ #![feature(no_core)] #![feature(lang_items)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] diff --git a/library/rtstartup/rsend.rs b/library/rtstartup/rsend.rs index 18ee7c19ba..d5aca80edf 100644 --- a/library/rtstartup/rsend.rs +++ b/library/rtstartup/rsend.rs @@ -2,8 +2,7 @@ #![feature(no_core)] #![feature(lang_items)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![crate_type = "rlib"] #![no_core] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 71d00b5c08..1c738761e8 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } libc = { version = "0.2.79", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.35" } +compiler_builtins = { version = "0.1.39" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.9.0", default-features = false, features = ['rustc-dep-of-std'] } @@ -60,7 +60,7 @@ panic-unwind = ["panic_unwind"] profiler = ["profiler_builtins"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] -compiler-builtins-asm = ["alloc/compiler-builtins-asm"] +compiler-builtins-no-asm = ["alloc/compiler-builtins-no-asm"] compiler-builtins-mangled-names = ["alloc/compiler-builtins-mangled-names"] llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 8491ff4003..843ef09a58 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -166,8 +166,9 @@ impl System { match old_layout.size() { 0 => self.alloc_impl(new_layout, zeroed), - // SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size` - // as required by safety conditions. Other conditions must be upheld by the caller + // SAFETY: `new_size` is non-zero as `new_size` is greater than or equal to `old_size` + // as required by safety conditions and the `old_size == 0` case was handled in the + // previous match arm. Other conditions must be upheld by the caller old_size if old_layout.align() == new_layout.align() => unsafe { let new_size = new_layout.size(); diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index 7e8e0a621e..0aae4674b2 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -95,11 +95,12 @@ mod tests; // a backtrace or actually symbolizing it. use crate::backtrace_rs::{self, BytesOrWideString}; +use crate::cell::UnsafeCell; use crate::env; use crate::ffi::c_void; use crate::fmt; use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -use crate::sync::Mutex; +use crate::sync::Once; use crate::sys_common::backtrace::{lock, output_filename}; use crate::vec::Vec; @@ -132,7 +133,7 @@ pub enum BacktraceStatus { enum Inner { Unsupported, Disabled, - Captured(Mutex), + Captured(LazilyResolvedCapture), } struct Capture { @@ -146,11 +147,14 @@ fn _assert_send_sync() { _assert::(); } -struct BacktraceFrame { +/// A single frame of a backtrace. +#[unstable(feature = "backtrace_frames", issue = "79676")] +pub struct BacktraceFrame { frame: RawFrame, symbols: Vec, } +#[derive(Debug)] enum RawFrame { Actual(backtrace_rs::Frame), #[cfg(test)] @@ -171,12 +175,11 @@ enum BytesOrWide { impl fmt::Debug for Backtrace { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut capture = match &self.inner { + let capture = match &self.inner { Inner::Unsupported => return fmt.write_str(""), Inner::Disabled => return fmt.write_str(""), - Inner::Captured(c) => c.lock().unwrap(), + Inner::Captured(c) => c.force(), }; - capture.resolve(); let frames = &capture.frames[capture.actual_start..]; @@ -196,6 +199,14 @@ impl fmt::Debug for Backtrace { } } +impl fmt::Debug for BacktraceFrame { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut dbg = fmt.debug_list(); + dbg.entries(&self.symbols); + dbg.finish() + } +} + impl fmt::Debug for BacktraceSymbol { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { // FIXME: improve formatting: https://github.com/rust-lang/rust/issues/65280 @@ -331,7 +342,7 @@ impl Backtrace { let inner = if frames.is_empty() { Inner::Unsupported } else { - Inner::Captured(Mutex::new(Capture { + Inner::Captured(LazilyResolvedCapture::new(Capture { actual_start: actual_start.unwrap_or(0), frames, resolved: false, @@ -353,14 +364,21 @@ impl Backtrace { } } +impl<'a> Backtrace { + /// Returns an iterator over the backtrace frames. + #[unstable(feature = "backtrace_frames", issue = "79676")] + pub fn frames(&'a self) -> &'a [BacktraceFrame] { + if let Inner::Captured(c) = &self.inner { &c.force().frames } else { &[] } + } +} + impl fmt::Display for Backtrace { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut capture = match &self.inner { + let capture = match &self.inner { Inner::Unsupported => return fmt.write_str("unsupported backtrace"), Inner::Disabled => return fmt.write_str("disabled backtrace"), - Inner::Captured(c) => c.lock().unwrap(), + Inner::Captured(c) => c.force(), }; - capture.resolve(); let full = fmt.alternate(); let (frames, style) = if full { @@ -404,6 +422,33 @@ impl fmt::Display for Backtrace { } } +struct LazilyResolvedCapture { + sync: Once, + capture: UnsafeCell, +} + +impl LazilyResolvedCapture { + fn new(capture: Capture) -> Self { + LazilyResolvedCapture { sync: Once::new(), capture: UnsafeCell::new(capture) } + } + + fn force(&self) -> &Capture { + self.sync.call_once(|| { + // SAFETY: This exclusive reference can't overlap with any others + // `Once` guarantees callers will block until this closure returns + // `Once` also guarantees only a single caller will enter this closure + unsafe { &mut *self.capture.get() }.resolve(); + }); + + // SAFETY: This shared reference can't overlap with the exclusive reference above + unsafe { &*self.capture.get() } + } +} + +// SAFETY: Access to the inner value is synchronized using a thread-safe `Once` +// So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too +unsafe impl Sync for LazilyResolvedCapture where Capture: Sync {} + impl Capture { fn resolve(&mut self) { // If we're already resolved, nothing to do! diff --git a/library/std/src/backtrace/tests.rs b/library/std/src/backtrace/tests.rs index f5f74d1eb9..f5da93f93f 100644 --- a/library/std/src/backtrace/tests.rs +++ b/library/std/src/backtrace/tests.rs @@ -1,48 +1,52 @@ use super::*; +fn generate_fake_frames() -> Vec { + vec![ + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![BacktraceSymbol { + name: Some(b"std::backtrace::Backtrace::create".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())), + lineno: Some(100), + colno: None, + }], + }, + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![BacktraceSymbol { + name: Some(b"__rust_maybe_catch_panic".to_vec()), + filename: None, + lineno: None, + colno: None, + }], + }, + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![ + BacktraceSymbol { + name: Some(b"std::rt::lang_start_internal".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), + lineno: Some(300), + colno: Some(5), + }, + BacktraceSymbol { + name: Some(b"std::rt::lang_start".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), + lineno: Some(400), + colno: None, + }, + ], + }, + ] +} + #[test] fn test_debug() { let backtrace = Backtrace { - inner: Inner::Captured(Mutex::new(Capture { + inner: Inner::Captured(LazilyResolvedCapture::new(Capture { actual_start: 1, resolved: true, - frames: vec![ - BacktraceFrame { - frame: RawFrame::Fake, - symbols: vec![BacktraceSymbol { - name: Some(b"std::backtrace::Backtrace::create".to_vec()), - filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())), - lineno: Some(100), - colno: None, - }], - }, - BacktraceFrame { - frame: RawFrame::Fake, - symbols: vec![BacktraceSymbol { - name: Some(b"__rust_maybe_catch_panic".to_vec()), - filename: None, - lineno: None, - colno: None, - }], - }, - BacktraceFrame { - frame: RawFrame::Fake, - symbols: vec![ - BacktraceSymbol { - name: Some(b"std::rt::lang_start_internal".to_vec()), - filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), - lineno: Some(300), - colno: Some(5), - }, - BacktraceSymbol { - name: Some(b"std::rt::lang_start".to_vec()), - filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), - lineno: Some(400), - colno: None, - }, - ], - }, - ], + frames: generate_fake_frames(), })), }; @@ -54,4 +58,38 @@ fn test_debug() { \n]"; assert_eq!(format!("{:#?}", backtrace), expected); + + // Format the backtrace a second time, just to make sure lazily resolved state is stable + assert_eq!(format!("{:#?}", backtrace), expected); +} + +#[test] +fn test_frames() { + let backtrace = Backtrace { + inner: Inner::Captured(LazilyResolvedCapture::new(Capture { + actual_start: 1, + resolved: true, + frames: generate_fake_frames(), + })), + }; + + let frames = backtrace.frames(); + + #[rustfmt::skip] + let expected = vec![ + "[ + { fn: \"std::backtrace::Backtrace::create\", file: \"rust/backtrace.rs\", line: 100 }, +]", + "[ + { fn: \"__rust_maybe_catch_panic\" }, +]", + "[ + { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 }, + { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 }, +]" + ]; + + let mut iter = frames.iter().zip(expected.iter()); + + assert!(iter.all(|(f, e)| format!("{:#?}", f) == *e)); } diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index ac72345dad..27f7191831 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -52,6 +52,9 @@ use crate::sys; /// hash, as determined by the [`Hash`] trait, or its equality, as determined by /// the [`Eq`] trait, changes while it is in the map. This is normally only /// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. +/// The behavior resulting from such a logic error is not specified, but will +/// not result in undefined behavior. This could include panics, incorrect results, +/// aborts, memory leaks, and non-termination. /// /// The hash table implementation is a Rust port of Google's [SwissTable]. /// The original C++ version of SwissTable can be found [here], and this @@ -195,7 +198,6 @@ use crate::sys; /// // use the values stored in map /// ``` -#[derive(Clone)] #[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashMap { @@ -449,6 +451,7 @@ impl HashMap { /// a.insert(1, "a"); /// assert_eq!(a.len(), 1); /// ``` + #[doc(alias = "length")] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { self.base.len() @@ -655,8 +658,7 @@ where /// down no lower than the supplied limit while maintaining the internal rules /// and possibly leaving some space in accordance with the resize policy. /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. + /// If the current capacity is less than the lower limit, this is a no-op. /// /// # Examples /// @@ -676,7 +678,6 @@ where #[inline] #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] pub fn shrink_to(&mut self, min_capacity: usize) { - assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity"); self.base.shrink_to(min_capacity); } @@ -858,6 +859,7 @@ where /// assert_eq!(map.remove(&1), Some("a")); /// assert_eq!(map.remove(&1), None); /// ``` + #[doc(alias = "delete")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(&mut self, k: &Q) -> Option @@ -1029,6 +1031,24 @@ where } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for HashMap +where + K: Clone, + V: Clone, + S: Clone, +{ + #[inline] + fn clone(&self) -> Self { + Self { base: self.base.clone() } + } + + #[inline] + fn clone_from(&mut self, other: &Self) { + self.base.clone_from(&other.base); + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for HashMap where diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 3299fd12e0..912e975aa0 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -37,7 +37,9 @@ use super::map::{map_try_reserve_error, RandomState}; /// item's hash, as determined by the [`Hash`] trait, or its equality, as /// determined by the [`Eq`] trait, changes while it is in the set. This is /// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or -/// unsafe code. +/// unsafe code. The behavior resulting from such a logic error is not +/// specified, but will not result in undefined behavior. This could include +/// panics, incorrect results, aborts, memory leaks, and non-termination. /// /// # Examples /// @@ -106,7 +108,6 @@ use super::map::{map_try_reserve_error, RandomState}; /// [`HashMap`]: crate::collections::HashMap /// [`RefCell`]: crate::cell::RefCell /// [`Cell`]: crate::cell::Cell -#[derive(Clone)] #[cfg_attr(not(test), rustc_diagnostic_item = "hashset_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashSet { @@ -200,6 +201,7 @@ impl HashSet { /// v.insert(1); /// assert_eq!(v.len(), 1); /// ``` + #[doc(alias = "length")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { @@ -460,9 +462,7 @@ where /// down no lower than the supplied limit while maintaining the internal rules /// and possibly leaving some space in accordance with the resize policy. /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. - /// + /// If the current capacity is less than the lower limit, this is a no-op. /// # Examples /// /// ``` @@ -874,6 +874,7 @@ where /// assert_eq!(set.remove(&2), true); /// assert_eq!(set.remove(&2), false); /// ``` + #[doc(alias = "delete")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(&mut self, value: &Q) -> bool @@ -932,6 +933,23 @@ where } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for HashSet +where + T: Clone, + S: Clone, +{ + #[inline] + fn clone(&self) -> Self { + Self { base: self.base.clone() } + } + + #[inline] + fn clone_from(&mut self, other: &Self) { + self.base.clone_from(&other.base); + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for HashSet where diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index a1aab767eb..80a13d52a2 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -110,10 +110,10 @@ //! //! For Sets, all operations have the cost of the equivalent Map operation. //! -//! | | get | insert | remove | predecessor | append | -//! |--------------|-----------|-----------|-----------|-------------|--------| -//! | [`HashMap`] | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | -//! | [`BTreeMap`] | O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) | O(n+m) | +//! | | get | insert | remove | range | append | +//! |--------------|-----------|-----------|-----------|-----------|--------| +//! | [`HashMap`] | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | +//! | [`BTreeMap`] | O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) | O(n+m) | //! //! # Correct and Efficient Usage of Collections //! diff --git a/library/std/src/env.rs b/library/std/src/env.rs index b0fceb9b2f..9763a2da34 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -561,6 +561,13 @@ pub fn home_dir() -> Option { /// Returns the path of a temporary directory. /// +/// The temporary directory may be shared among users, or between processes +/// with different privileges; thus, the creation of any files or directories +/// in the temporary directory must use a secure method to create a uniquely +/// named file. Creating a file or directory with a fixed or predictable name +/// may result in "insecure temporary file" security vulnerabilities. Consider +/// using a crate that securely creates temporary files or directories. +/// /// # Unix /// /// Returns the value of the `TMPDIR` environment variable if it is @@ -580,14 +587,10 @@ pub fn home_dir() -> Option { /// /// ```no_run /// use std::env; -/// use std::fs::File; /// -/// fn main() -> std::io::Result<()> { +/// fn main() { /// let mut dir = env::temp_dir(); -/// dir.push("foo.txt"); -/// -/// let f = File::create(dir)?; -/// Ok(()) +/// println!("Temporary directory: {}", dir.display()); /// } /// ``` #[stable(feature = "env", since = "1.0.0")] diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 0044e59d69..605d953f5d 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -42,8 +42,6 @@ use crate::string; /// via [`Error::source()`]. This makes it possible for the high-level /// module to provide its own errors while also revealing some of the /// implementation for debugging via `source` chains. -/// -/// [`Result`]: Result #[stable(feature = "rust1", since = "1.0.0")] pub trait Error: Debug + Display { /// The lower-level source of this error, if any. @@ -488,6 +486,27 @@ impl Error for Box { } } +#[stable(feature = "error_by_ref", since = "1.51.0")] +impl<'a, T: Error + ?Sized> Error for &'a T { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + Error::description(&**self) + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn Error> { + Error::cause(&**self) + } + + fn source(&self) -> Option<&(dyn Error + 'static)> { + Error::source(&**self) + } + + fn backtrace(&self) -> Option<&Backtrace> { + Error::backtrace(&**self) + } +} + #[stable(feature = "fmt_error", since = "1.11.0")] impl Error for fmt::Error { #[allow(deprecated)] diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index c30458c054..f51b2c2462 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -1,12 +1,13 @@ -//! This module provides constants which are specific to the implementation -//! of the `f32` floating point data type. +//! Constants specific to the `f32` single-precision floating point type. //! //! *[See also the `f32` primitive type](primitive@f32).* //! //! Mathematically significant numbers are provided in the `consts` sub-module. //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! For the constants defined directly in this module +//! (as distinct from those defined in the `consts` sub-module), +//! new code should instead use the associated constants +//! defined directly on the `f32` type. #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] @@ -20,15 +21,11 @@ use crate::intrinsics; use crate::sys::cmath; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::f32::consts; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f32::{DIGITS, EPSILON, MANTISSA_DIGITS, RADIX}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f32::{INFINITY, MAX_10_EXP, NAN, NEG_INFINITY}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f32::{MAX, MIN, MIN_POSITIVE}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f32::{MAX_EXP, MIN_10_EXP, MIN_EXP}; +#[allow(deprecated, deprecated_in_future)] +pub use core::f32::{ + consts, DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, + MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, +}; #[cfg(not(test))] #[lang = "f32_runtime"] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index f4cc53979d..8c41e44868 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -1,12 +1,13 @@ -//! This module provides constants which are specific to the implementation -//! of the `f64` floating point data type. +//! Constants specific to the `f64` double-precision floating point type. //! //! *[See also the `f64` primitive type](primitive@f64).* //! //! Mathematically significant numbers are provided in the `consts` sub-module. //! -//! Although using these constants won’t cause compilation warnings, -//! new code should use the associated constants directly on the primitive type. +//! For the constants defined directly in this module +//! (as distinct from those defined in the `consts` sub-module), +//! new code should instead use the associated constants +//! defined directly on the `f64` type. #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] @@ -20,15 +21,11 @@ use crate::intrinsics; use crate::sys::cmath; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::f64::consts; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f64::{DIGITS, EPSILON, MANTISSA_DIGITS, RADIX}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f64::{INFINITY, MAX_10_EXP, NAN, NEG_INFINITY}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f64::{MAX, MIN, MIN_POSITIVE}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::f64::{MAX_EXP, MIN_10_EXP, MIN_EXP}; +#[allow(deprecated, deprecated_in_future)] +pub use core::f64::{ + consts, DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, + MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, +}; #[cfg(not(test))] #[lang = "f64_runtime"] diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index 60b642a6db..230ef0b23d 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -86,7 +86,7 @@ use crate::sys; /// use std::ffi::CString; /// use std::os::raw::c_char; /// -/// extern { +/// extern "C" { /// fn my_printer(s: *const c_char); /// } /// @@ -144,7 +144,7 @@ pub struct CString { /// use std::ffi::CStr; /// use std::os::raw::c_char; /// -/// extern { fn my_string() -> *const c_char; } +/// extern "C" { fn my_string() -> *const c_char; } /// /// unsafe { /// let slice = CStr::from_ptr(my_string()); @@ -159,7 +159,7 @@ pub struct CString { /// use std::os::raw::c_char; /// /// fn work(data: &CStr) { -/// extern { fn work_with(data: *const c_char); } +/// extern "C" { fn work_with(data: *const c_char); } /// /// unsafe { work_with(data.as_ptr()) } /// } @@ -174,7 +174,7 @@ pub struct CString { /// use std::ffi::CStr; /// use std::os::raw::c_char; /// -/// extern { fn my_string() -> *const c_char; } +/// extern "C" { fn my_string() -> *const c_char; } /// /// fn my_string_safe() -> String { /// unsafe { @@ -359,7 +359,7 @@ impl CString { /// use std::ffi::CString; /// use std::os::raw::c_char; /// - /// extern { fn puts(s: *const c_char); } + /// extern "C" { fn puts(s: *const c_char); } /// /// let to_print = CString::new("Hello!").expect("CString::new failed"); /// unsafe { @@ -465,7 +465,7 @@ impl CString { /// use std::ffi::CString; /// use std::os::raw::c_char; /// - /// extern { + /// extern "C" { /// fn some_extern_function(s: *mut c_char); /// } /// @@ -1147,7 +1147,7 @@ impl CStr { /// use std::ffi::CStr; /// use std::os::raw::c_char; /// - /// extern { + /// extern "C" { /// fn my_string() -> *const c_char; /// } /// diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 5d93016cad..c9c8f68cd9 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -111,6 +111,7 @@ impl OsString { /// let os_string = OsString::new(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn new() -> OsString { OsString { inner: Buf::from_string(String::new()) } } @@ -127,6 +128,7 @@ impl OsString { /// assert_eq!(os_string.as_os_str(), os_str); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn as_os_str(&self) -> &OsStr { self } @@ -145,6 +147,7 @@ impl OsString { /// assert_eq!(string, Ok(String::from("foo"))); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn into_string(self) -> Result { self.inner.into_string().map_err(|buf| OsString { inner: buf }) } @@ -163,6 +166,7 @@ impl OsString { /// assert_eq!(&os_string, "foobar"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn push>(&mut self, s: T) { self.inner.push_slice(&s.as_ref().inner) } @@ -189,6 +193,7 @@ impl OsString { /// assert_eq!(capacity, os_string.capacity()); /// ``` #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + #[inline] pub fn with_capacity(capacity: usize) -> OsString { OsString { inner: Buf::with_capacity(capacity) } } @@ -207,6 +212,7 @@ impl OsString { /// assert_eq!(&os_string, ""); /// ``` #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + #[inline] pub fn clear(&mut self) { self.inner.clear() } @@ -224,6 +230,7 @@ impl OsString { /// assert!(os_string.capacity() >= 10); /// ``` #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + #[inline] pub fn capacity(&self) -> usize { self.inner.capacity() } @@ -243,6 +250,7 @@ impl OsString { /// assert!(s.capacity() >= 10); /// ``` #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + #[inline] pub fn reserve(&mut self, additional: usize) { self.inner.reserve(additional) } @@ -265,6 +273,7 @@ impl OsString { /// assert!(s.capacity() >= 10); /// ``` #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + #[inline] pub fn reserve_exact(&mut self, additional: usize) { self.inner.reserve_exact(additional) } @@ -285,6 +294,7 @@ impl OsString { /// assert_eq!(3, s.capacity()); /// ``` #[stable(feature = "osstring_shrink_to_fit", since = "1.19.0")] + #[inline] pub fn shrink_to_fit(&mut self) { self.inner.shrink_to_fit() } @@ -294,8 +304,7 @@ impl OsString { /// The capacity will remain at least as large as both the length /// and the supplied value. /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. + /// If the current capacity is less than the lower limit, this is a no-op. /// /// # Examples /// @@ -342,6 +351,7 @@ impl From for OsString { /// Converts a [`String`] into a [`OsString`]. /// /// The conversion copies the data, and includes an allocation on the heap. + #[inline] fn from(s: String) -> OsString { OsString { inner: Buf::from_string(s) } } @@ -408,6 +418,7 @@ impl fmt::Debug for OsString { #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for OsString { + #[inline] fn eq(&self, other: &OsString) -> bool { &**self == &**other } @@ -415,6 +426,7 @@ impl PartialEq for OsString { #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for OsString { + #[inline] fn eq(&self, other: &str) -> bool { &**self == other } @@ -422,6 +434,7 @@ impl PartialEq for OsString { #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for str { + #[inline] fn eq(&self, other: &OsString) -> bool { &**other == self } @@ -429,6 +442,7 @@ impl PartialEq for str { #[stable(feature = "os_str_str_ref_eq", since = "1.29.0")] impl PartialEq<&str> for OsString { + #[inline] fn eq(&self, other: &&str) -> bool { **self == **other } @@ -436,6 +450,7 @@ impl PartialEq<&str> for OsString { #[stable(feature = "os_str_str_ref_eq", since = "1.29.0")] impl<'a> PartialEq for &'a str { + #[inline] fn eq(&self, other: &OsString) -> bool { **other == **self } @@ -539,6 +554,7 @@ impl OsStr { /// assert_eq!(os_str.to_str(), Some("foo")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn to_str(&self) -> Option<&str> { self.inner.to_str() } @@ -589,6 +605,7 @@ impl OsStr { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn to_string_lossy(&self) -> Cow<'_, str> { self.inner.to_string_lossy() } @@ -605,6 +622,7 @@ impl OsStr { /// assert_eq!(os_string, OsString::from("foo")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn to_os_string(&self) -> OsString { OsString { inner: self.inner.to_owned() } } @@ -653,7 +671,9 @@ impl OsStr { /// let os_str = OsStr::new("foo"); /// assert_eq!(os_str.len(), 3); /// ``` + #[doc(alias = "length")] #[stable(feature = "osstring_simple_functions", since = "1.9.0")] + #[inline] pub fn len(&self) -> usize { self.inner.inner.len() } @@ -695,6 +715,7 @@ impl OsStr { /// assert_eq!("grÜße, jÜrgen ❤", s); /// ``` #[unstable(feature = "osstring_ascii", issue = "70516")] + #[inline] pub fn make_ascii_lowercase(&mut self) { self.inner.make_ascii_lowercase() } @@ -720,6 +741,7 @@ impl OsStr { /// assert_eq!("GRüßE, JüRGEN ❤", s); /// ``` #[unstable(feature = "osstring_ascii", issue = "70516")] + #[inline] pub fn make_ascii_uppercase(&mut self) { self.inner.make_ascii_uppercase() } @@ -783,6 +805,7 @@ impl OsStr { /// assert!(!non_ascii.is_ascii()); /// ``` #[unstable(feature = "osstring_ascii", issue = "70516")] + #[inline] pub fn is_ascii(&self) -> bool { self.inner.is_ascii() } @@ -803,13 +826,14 @@ impl OsStr { /// assert!(!OsString::from("Ferrös").eq_ignore_ascii_case("FERRÖS")); /// ``` #[unstable(feature = "osstring_ascii", issue = "70516")] - pub fn eq_ignore_ascii_case>(&self, other: &S) -> bool { + pub fn eq_ignore_ascii_case>(&self, other: S) -> bool { self.inner.eq_ignore_ascii_case(&other.as_ref().inner) } } #[stable(feature = "box_from_os_str", since = "1.17.0")] impl From<&OsStr> for Box { + #[inline] fn from(s: &OsStr) -> Box { let rw = Box::into_raw(s.inner.into_box()) as *mut OsStr; unsafe { Box::from_raw(rw) } @@ -831,6 +855,7 @@ impl From> for Box { impl From> for OsString { /// Converts a [`Box`]`<`[`OsStr`]`>` into a `OsString` without copying or /// allocating. + #[inline] fn from(boxed: Box) -> OsString { boxed.into_os_string() } @@ -839,6 +864,7 @@ impl From> for OsString { #[stable(feature = "box_from_os_string", since = "1.20.0")] impl From for Box { /// Converts a [`OsString`] into a [`Box`]`` without copying or allocating. + #[inline] fn from(s: OsString) -> Box { s.into_boxed_os_str() } @@ -924,6 +950,7 @@ impl<'a> From> for OsString { #[stable(feature = "box_default_extra", since = "1.17.0")] impl Default for Box { + #[inline] fn default() -> Box { let rw = Box::into_raw(Slice::empty_box()) as *mut OsStr; unsafe { Box::from_raw(rw) } @@ -1074,6 +1101,7 @@ impl OsStr { #[stable(feature = "rust1", since = "1.0.0")] impl Borrow for OsString { + #[inline] fn borrow(&self) -> &OsStr { &self[..] } @@ -1082,9 +1110,11 @@ impl Borrow for OsString { #[stable(feature = "rust1", since = "1.0.0")] impl ToOwned for OsStr { type Owned = OsString; + #[inline] fn to_owned(&self) -> OsString { self.to_os_string() } + #[inline] fn clone_into(&self, target: &mut OsString) { self.inner.clone_into(&mut target.inner) } @@ -1092,6 +1122,7 @@ impl ToOwned for OsStr { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for OsStr { + #[inline] fn as_ref(&self) -> &OsStr { self } @@ -1122,12 +1153,14 @@ impl AsRef for String { } impl FromInner for OsString { + #[inline] fn from_inner(buf: Buf) -> OsString { OsString { inner: buf } } } impl IntoInner for OsString { + #[inline] fn into_inner(self) -> Buf { self.inner } @@ -1144,6 +1177,7 @@ impl AsInner for OsStr { impl FromStr for OsString { type Err = core::convert::Infallible; + #[inline] fn from_str(s: &str) -> Result { Ok(OsString::from(s)) } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index e2d4f2e6a5..43119c36cf 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1524,6 +1524,7 @@ impl AsInner for DirEntry { /// Ok(()) /// } /// ``` +#[doc(alias = "delete")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove_file>(path: P) -> io::Result<()> { fs_imp::unlink(path.as_ref()) @@ -1958,6 +1959,7 @@ pub fn create_dir_all>(path: P) -> io::Result<()> { /// Ok(()) /// } /// ``` +#[doc(alias = "delete")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove_dir>(path: P) -> io::Result<()> { fs_imp::rmdir(path.as_ref()) @@ -1995,6 +1997,7 @@ pub fn remove_dir>(path: P) -> io::Result<()> { /// Ok(()) /// } /// ``` +#[doc(alias = "delete")] #[stable(feature = "rust1", since = "1.0.0")] pub fn remove_dir_all>(path: P) -> io::Result<()> { fs_imp::remove_dir_all(path.as_ref()) diff --git a/library/std/src/future.rs b/library/std/src/future.rs deleted file mode 100644 index 9d9c36e9af..0000000000 --- a/library/std/src/future.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Asynchronous values. - -#[doc(inline)] -#[stable(feature = "futures_api", since = "1.36.0")] -pub use core::future::Future; - -#[doc(inline)] -#[unstable(feature = "gen_future", issue = "50547")] -pub use core::future::{from_generator, get_context, ResumeTy}; - -#[doc(inline)] -#[stable(feature = "future_readiness_fns", since = "1.48.0")] -pub use core::future::{pending, ready, Pending, Ready}; - -#[doc(inline)] -#[unstable(feature = "into_future", issue = "67644")] -pub use core::future::IntoFuture; diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 16c18d6e14..987371f50e 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -271,6 +271,20 @@ impl Read for BufReader { Ok(nread) } + // Small read_exacts from a BufReader are extremely common when used with a deserializer. + // The default implementation calls read in a loop, which results in surprisingly poor code + // generation for the common path where the buffer has enough bytes to fill the passed-in + // buffer. + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + if self.buffer().len() >= buf.len() { + buf.copy_from_slice(&self.buffer()[..buf.len()]); + self.consume(buf.len()); + return Ok(()); + } + + crate::io::default_read_exact(self, buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { let total_len = bufs.iter().map(|b| b.len()).sum::(); if self.pos == self.cap && total_len >= self.buf.len() { @@ -396,7 +410,6 @@ impl Seek for BufReader { /// # Example /// /// ```no_run - /// #![feature(seek_convenience)] /// use std::{ /// io::{self, BufRead, BufReader, Seek}, /// fs::File, diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 3b3399860b..65bc2fcf00 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -1,7 +1,9 @@ +use crate::error; use crate::fmt; use crate::io::{ self, Error, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE, }; +use crate::mem; /// Wraps a writer and buffers its output. /// @@ -115,7 +117,7 @@ impl BufWriter { /// "successfully written" (by returning nonzero success values from /// `write`), any 0-length writes from `inner` must be reported as i/o /// errors from this method. - pub(super) fn flush_buf(&mut self) -> io::Result<()> { + pub(in crate::io) fn flush_buf(&mut self) -> io::Result<()> { /// Helper struct to ensure the buffer is updated after all the writes /// are complete. It tracks the number of written bytes and drains them /// all from the front of the buffer when dropped. @@ -241,6 +243,18 @@ impl BufWriter { &self.buf } + /// Returns a mutable reference to the internal buffer. + /// + /// This can be used to write data directly into the buffer without triggering writers + /// to the underlying writer. + /// + /// That the buffer is a `Vec` is an implementation detail. + /// Callers should not modify the capacity as there currently is no public API to do so + /// and thus any capacity changes would be unexpected by the user. + pub(in crate::io) fn buffer_mut(&mut self) -> &mut Vec { + &mut self.buf + } + /// Returns the number of bytes the internal buffer can hold without flushing. /// /// # Examples @@ -287,6 +301,103 @@ impl BufWriter { Ok(()) => Ok(self.inner.take().unwrap()), } } + + /// Disassembles this `BufWriter`, returning the underlying writer, and any buffered but + /// unwritten data. + /// + /// If the underlying writer panicked, it is not known what portion of the data was written. + /// In this case, we return `WriterPanicked` for the buffered data (from which the buffer + /// contents can still be recovered). + /// + /// `into_raw_parts` makes no attempt to flush data and cannot fail. + /// + /// # Examples + /// + /// ``` + /// #![feature(bufwriter_into_raw_parts)] + /// use std::io::{BufWriter, Write}; + /// + /// let mut buffer = [0u8; 10]; + /// let mut stream = BufWriter::new(buffer.as_mut()); + /// write!(stream, "too much data").unwrap(); + /// stream.flush().expect_err("it doesn't fit"); + /// let (recovered_writer, buffered_data) = stream.into_raw_parts(); + /// assert_eq!(recovered_writer.len(), 0); + /// assert_eq!(&buffered_data.unwrap(), b"ata"); + /// ``` + #[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] + pub fn into_raw_parts(mut self) -> (W, Result, WriterPanicked>) { + let buf = mem::take(&mut self.buf); + let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; + (self.inner.take().unwrap(), buf) + } +} + +#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] +/// Error returned for the buffered data from `BufWriter::into_raw_parts`, when the underlying +/// writer has previously panicked. Contains the (possibly partly written) buffered data. +/// +/// # Example +/// +/// ``` +/// #![feature(bufwriter_into_raw_parts)] +/// use std::io::{self, BufWriter, Write}; +/// use std::panic::{catch_unwind, AssertUnwindSafe}; +/// +/// struct PanickingWriter; +/// impl Write for PanickingWriter { +/// fn write(&mut self, buf: &[u8]) -> io::Result { panic!() } +/// fn flush(&mut self) -> io::Result<()> { panic!() } +/// } +/// +/// let mut stream = BufWriter::new(PanickingWriter); +/// write!(stream, "some data").unwrap(); +/// let result = catch_unwind(AssertUnwindSafe(|| { +/// stream.flush().unwrap() +/// })); +/// assert!(result.is_err()); +/// let (recovered_writer, buffered_data) = stream.into_raw_parts(); +/// assert!(matches!(recovered_writer, PanickingWriter)); +/// assert_eq!(buffered_data.unwrap_err().into_inner(), b"some data"); +/// ``` +pub struct WriterPanicked { + buf: Vec, +} + +impl WriterPanicked { + /// Returns the perhaps-unwritten data. Some of this data may have been written by the + /// panicking call(s) to the underlying writer, so simply writing it again is not a good idea. + #[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] + pub fn into_inner(self) -> Vec { + self.buf + } + + const DESCRIPTION: &'static str = + "BufWriter inner writer panicked, what data remains unwritten is not known"; +} + +#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] +impl error::Error for WriterPanicked { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + Self::DESCRIPTION + } +} + +#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] +impl fmt::Display for WriterPanicked { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", Self::DESCRIPTION) + } +} + +#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] +impl fmt::Debug for WriterPanicked { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WriterPanicked") + .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) + .finish() + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs index 66a64f667b..f6c2b49956 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/std/src/io/buffered/tests.rs @@ -443,6 +443,18 @@ fn bench_buffered_reader(b: &mut test::Bencher) { b.iter(|| BufReader::new(io::empty())); } +#[bench] +fn bench_buffered_reader_small_reads(b: &mut test::Bencher) { + let data = (0..u8::MAX).cycle().take(1024 * 4).collect::>(); + b.iter(|| { + let mut reader = BufReader::new(&data[..]); + let mut buf = [0u8; 4]; + for _ in 0..1024 { + reader.read_exact(&mut buf).unwrap(); + } + }); +} + #[bench] fn bench_buffered_writer(b: &mut test::Bencher) { b.iter(|| BufWriter::new(io::sink())); diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index b88bca2f2b..3780f2044c 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,4 +1,4 @@ -use crate::io::{self, ErrorKind, Read, Write}; +use super::{BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE}; use crate::mem::MaybeUninit; /// Copies the entire contents of a reader into a writer. @@ -40,7 +40,7 @@ use crate::mem::MaybeUninit; /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn copy(reader: &mut R, writer: &mut W) -> io::Result +pub fn copy(reader: &mut R, writer: &mut W) -> Result where R: Read, W: Write, @@ -54,14 +54,82 @@ where } } -/// The general read-write-loop implementation of -/// `io::copy` that is used when specializations are not available or not applicable. -pub(crate) fn generic_copy(reader: &mut R, writer: &mut W) -> io::Result +/// The userspace read-write-loop implementation of `io::copy` that is used when +/// OS-specific specializations for copy offloading are not available or not applicable. +pub(crate) fn generic_copy(reader: &mut R, writer: &mut W) -> Result where R: Read, W: Write, { - let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit(); + BufferedCopySpec::copy_to(reader, writer) +} + +/// Specialization of the read-write loop that either uses a stack buffer +/// or reuses the internal buffer of a BufWriter +trait BufferedCopySpec: Write { + fn copy_to(reader: &mut R, writer: &mut Self) -> Result; +} + +impl BufferedCopySpec for W { + default fn copy_to(reader: &mut R, writer: &mut Self) -> Result { + stack_buffer_copy(reader, writer) + } +} + +impl BufferedCopySpec for BufWriter { + fn copy_to(reader: &mut R, writer: &mut Self) -> Result { + if writer.capacity() < DEFAULT_BUF_SIZE { + return stack_buffer_copy(reader, writer); + } + + // FIXME: #42788 + // + // - This creates a (mut) reference to a slice of + // _uninitialized_ integers, which is **undefined behavior** + // + // - Only the standard library gets to soundly "ignore" this, + // based on its privileged knowledge of unstable rustc + // internals; + unsafe { + let spare_cap = writer.buffer_mut().spare_capacity_mut(); + reader.initializer().initialize(MaybeUninit::slice_assume_init_mut(spare_cap)); + } + + let mut len = 0; + + loop { + let buf = writer.buffer_mut(); + let spare_cap = buf.spare_capacity_mut(); + + if spare_cap.len() >= DEFAULT_BUF_SIZE { + match reader.read(unsafe { MaybeUninit::slice_assume_init_mut(spare_cap) }) { + Ok(0) => return Ok(len), // EOF reached + Ok(bytes_read) => { + assert!(bytes_read <= spare_cap.len()); + // Safety: The initializer contract guarantees that either it or `read` + // will have initialized these bytes. And we just checked that the number + // of bytes is within the buffer capacity. + unsafe { buf.set_len(buf.len() + bytes_read) }; + len += bytes_read as u64; + // Read again if the buffer still has enough capacity, as BufWriter itself would do + // This will occur if the reader returns short reads + continue; + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + } + } + + writer.flush_buf()?; + } + } +} + +fn stack_buffer_copy( + reader: &mut R, + writer: &mut W, +) -> Result { + let mut buf = MaybeUninit::<[u8; DEFAULT_BUF_SIZE]>::uninit(); // FIXME: #42788 // // - This creates a (mut) reference to a slice of diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 30d80b76ab..db3b0e2628 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -240,7 +240,6 @@ //! //! [`File`]: crate::fs::File //! [`TcpStream`]: crate::net::TcpStream -//! [`Vec`]: Vec //! [`io::stdout`]: stdout //! [`io::Result`]: self::Result //! [`?` operator]: ../../book/appendix-02-operators.html @@ -417,6 +416,25 @@ where write(buf) } +pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { + while !buf.is_empty() { + match this.read(buf) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer")) + } else { + Ok(()) + } +} + /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. @@ -767,23 +785,8 @@ pub trait Read { /// } /// ``` #[stable(feature = "read_exact", since = "1.6.0")] - fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> { - while !buf.is_empty() { - match self.read(buf) { - Ok(0) => break, - Ok(n) => { - let tmp = buf; - buf = &mut tmp[n..]; - } - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - if !buf.is_empty() { - Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer")) - } else { - Ok(()) - } + fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { + default_read_exact(self, buf) } /// Creates a "by reference" adaptor for this instance of `Read`. @@ -943,6 +946,54 @@ pub trait Read { } } +/// Read all bytes from a [reader][Read] into a new [`String`]. +/// +/// This is a convenience function for [`Read::read_to_string`]. Using this +/// function avoids having to create a variable first and provides more type +/// safety since you can only get the buffer out if there were no errors. (If you +/// use [`Read::read_to_string`] you have to remember to check whether the read +/// succeeded because otherwise your buffer will be empty or only partially full.) +/// +/// # Performance +/// +/// The downside of this function's increased ease of use and type safety is +/// that it gives you less control over performance. For example, you can't +/// pre-allocate memory like you can using [`String::with_capacity`] and +/// [`Read::read_to_string`]. Also, you can't re-use the buffer if an error +/// occurs while reading. +/// +/// In many cases, this function's performance will be adequate and the ease of use +/// and type safety tradeoffs will be worth it. However, there are cases where you +/// need more control over performance, and in those cases you should definitely use +/// [`Read::read_to_string`] directly. +/// +/// # Errors +/// +/// This function forces you to handle errors because the output (the `String`) +/// is wrapped in a [`Result`]. See [`Read::read_to_string`] for the errors +/// that can occur. If any error occurs, you will get an [`Err`], so you +/// don't have to worry about your buffer being empty or partially full. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(io_read_to_string)] +/// +/// # use std::io; +/// fn main() -> io::Result<()> { +/// let stdin = io::read_to_string(&mut io::stdin())?; +/// println!("Stdin was:"); +/// println!("{}", stdin); +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "io_read_to_string", issue = "80218")] +pub fn read_to_string(reader: &mut R) -> Result { + let mut buf = String::new(); + reader.read_to_string(&mut buf)?; + Ok(buf) +} + /// A buffer type used with `Read::read_vectored`. /// /// It is semantically a wrapper around an `&mut [u8]`, but is guaranteed to be @@ -1620,7 +1671,7 @@ pub trait Seek { /// # Example /// /// ```no_run - /// #![feature(seek_convenience)] + /// #![feature(seek_stream_len)] /// use std::{ /// io::{self, Seek}, /// fs::File, @@ -1634,7 +1685,7 @@ pub trait Seek { /// Ok(()) /// } /// ``` - #[unstable(feature = "seek_convenience", issue = "59359")] + #[unstable(feature = "seek_stream_len", issue = "59359")] fn stream_len(&mut self) -> Result { let old_pos = self.stream_position()?; let len = self.seek(SeekFrom::End(0))?; @@ -1655,7 +1706,6 @@ pub trait Seek { /// # Example /// /// ```no_run - /// #![feature(seek_convenience)] /// use std::{ /// io::{self, BufRead, BufReader, Seek}, /// fs::File, @@ -1672,7 +1722,7 @@ pub trait Seek { /// Ok(()) /// } /// ``` - #[unstable(feature = "seek_convenience", issue = "59359")] + #[stable(feature = "seek_convenience", since = "1.51.0")] fn stream_position(&mut self) -> Result { self.seek(SeekFrom::Current(0)) } @@ -1982,7 +2032,6 @@ pub trait BufRead: Read { /// also yielded an error. /// /// [`io::Result`]: self::Result - /// [`Vec`]: Vec /// [`read_until`]: BufRead::read_until /// /// # Examples diff --git a/library/std/src/io/prelude.rs b/library/std/src/io/prelude.rs index 3baab2be37..d80643101f 100644 --- a/library/std/src/io/prelude.rs +++ b/library/std/src/io/prelude.rs @@ -1,4 +1,4 @@ -//! The I/O Prelude +//! The I/O Prelude. //! //! The purpose of this module is to alleviate imports of many common I/O traits //! by adding a glob import to the top of I/O heavy modules: diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index db845457c9..e43ce4cdb4 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -4,7 +4,7 @@ mod tests; use crate::fmt; -use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Write}; +use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; /// A reader which is always at EOF. /// @@ -58,6 +58,21 @@ impl BufRead for Empty { fn consume(&mut self, _n: usize) {} } +#[stable(feature = "empty_seek", since = "1.51.0")] +impl Seek for Empty { + fn seek(&mut self, _pos: SeekFrom) -> io::Result { + Ok(0) + } + + fn stream_len(&mut self) -> io::Result { + Ok(0) + } + + fn stream_position(&mut self) -> io::Result { + Ok(0) + } +} + #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Empty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index 9450b1ee12..7632eaf872 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -1,5 +1,8 @@ +use crate::cmp::{max, min}; use crate::io::prelude::*; -use crate::io::{copy, empty, repeat, sink, Empty, Repeat, Sink}; +use crate::io::{ + copy, empty, repeat, sink, BufWriter, Empty, Repeat, Result, SeekFrom, Sink, DEFAULT_BUF_SIZE, +}; #[test] fn copy_copies() { @@ -11,6 +14,51 @@ fn copy_copies() { assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17); } +struct ShortReader { + cap: usize, + read_size: usize, + observed_buffer: usize, +} + +impl Read for ShortReader { + fn read(&mut self, buf: &mut [u8]) -> Result { + let bytes = min(self.cap, self.read_size); + self.cap -= bytes; + self.observed_buffer = max(self.observed_buffer, buf.len()); + Ok(bytes) + } +} + +struct WriteObserver { + observed_buffer: usize, +} + +impl Write for WriteObserver { + fn write(&mut self, buf: &[u8]) -> Result { + self.observed_buffer = max(self.observed_buffer, buf.len()); + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +#[test] +fn copy_specializes_bufwriter() { + let cap = 117 * 1024; + let buf_sz = 16 * 1024; + let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; + let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 }); + assert_eq!( + copy(&mut r, &mut w).unwrap(), + cap as u64, + "expected the whole capacity to be copied" + ); + assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader"); + assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes"); +} + #[test] fn sink_sinks() { let mut s = sink(); @@ -29,6 +77,26 @@ fn empty_reads() { assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0); } +#[test] +fn empty_seeks() { + let mut e = empty(); + assert!(matches!(e.seek(SeekFrom::Start(0)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Start(1)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Start(u64::MAX)), Ok(0))); + + assert!(matches!(e.seek(SeekFrom::End(i64::MIN)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::End(-1)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::End(0)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::End(1)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::End(i64::MAX)), Ok(0))); + + assert!(matches!(e.seek(SeekFrom::Current(i64::MIN)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Current(-1)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Current(0)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Current(1)), Ok(0))); + assert!(matches!(e.seek(SeekFrom::Current(i64::MAX)), Ok(0))); +} + #[test] fn repeat_repeats() { let mut r = repeat(4); diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 417a54e9df..6b06539a09 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -401,7 +401,7 @@ mod enum_keyword {} /// /// ```rust /// #[no_mangle] -/// pub extern fn callable_from_c(x: i32) -> bool { +/// pub extern "C" fn callable_from_c(x: i32) -> bool { /// x % 3 == 0 /// } /// ``` diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5bc5ddaa5f..961cff661e 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -175,7 +175,7 @@ //! [`str`]: prim@str //! [`mpsc`]: sync::mpsc //! [`std::cmp`]: cmp -//! [`std::slice`]: slice +//! [`std::slice`]: mod@slice //! [`use std::env`]: env/index.html //! [`use`]: ../book/ch07-02-defining-modules-to-control-scope-and-privacy.html //! [crates.io]: https://crates.io @@ -185,7 +185,8 @@ //! [other]: #what-is-in-the-standard-library-documentation //! [primitive types]: ../book/ch03-02-data-types.html //! [rust-discord]: https://discord.gg/rust-lang - +#![cfg_attr(not(bootstrap), doc = "[array]: prim@array")] +#![cfg_attr(not(bootstrap), doc = "[slice]: prim@slice")] #![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] #![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))] #![doc( @@ -223,6 +224,7 @@ #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] +#![feature(async_stream)] #![feature(arbitrary_self_types)] #![feature(array_error_internals)] #![feature(asm)] @@ -257,6 +259,7 @@ #![feature(dropck_eyepatch)] #![feature(duration_constants)] #![feature(duration_zero)] +#![feature(edition_panic)] #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] #![feature(extend_one)] @@ -281,6 +284,7 @@ #![feature(maybe_uninit_extra)] #![feature(maybe_uninit_ref)] #![feature(maybe_uninit_slice)] +#![feature(maybe_uninit_uninit_array)] #![feature(min_specialization)] #![feature(needs_panic_runtime)] #![feature(negative_impls)] @@ -288,8 +292,7 @@ #![feature(nll)] #![feature(nonnull_slice_from_raw_parts)] #![feature(once_cell)] -#![cfg_attr(bootstrap, feature(optin_builtin_traits))] -#![cfg_attr(not(bootstrap), feature(auto_traits))] +#![feature(auto_traits)] #![feature(or_patterns)] #![feature(panic_info_message)] #![feature(panic_internals)] @@ -298,7 +301,6 @@ #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(raw)] -#![feature(raw_ref_macros)] #![feature(ready_macro)] #![feature(rustc_attrs)] #![feature(rustc_private)] @@ -307,7 +309,6 @@ #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] -#![feature(slice_strip)] #![feature(staged_api)] #![feature(std_internals)] #![feature(stdsimd)] @@ -327,7 +328,7 @@ #![feature(unsafe_cell_raw_get)] #![feature(unwind_attributes)] #![feature(vec_into_raw_parts)] -#![feature(wake_trait)] +#![feature(vec_spare_capacity)] // NB: the above list is sorted to minimize merge conflicts. #![default_lib_allocator] @@ -407,23 +408,31 @@ pub use core::cmp; pub use core::convert; #[stable(feature = "rust1", since = "1.0.0")] pub use core::default; +#[stable(feature = "futures_api", since = "1.36.0")] +pub use core::future; #[stable(feature = "rust1", since = "1.0.0")] pub use core::hash; #[stable(feature = "core_hint", since = "1.27.0")] pub use core::hint; #[stable(feature = "i128", since = "1.26.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::i128; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::i16; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::i32; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::i64; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::i8; #[stable(feature = "rust1", since = "1.0.0")] pub use core::intrinsics; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::isize; #[stable(feature = "rust1", since = "1.0.0")] pub use core::iter; @@ -443,17 +452,25 @@ pub use core::ptr; pub use core::raw; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; +#[unstable(feature = "async_stream", issue = "79024")] +pub use core::stream; #[stable(feature = "i128", since = "1.26.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::u128; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::u16; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::u32; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::u64; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::u8; #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] pub use core::usize; pub mod f32; @@ -490,13 +507,10 @@ pub mod task { pub use core::task::*; #[doc(inline)] - #[unstable(feature = "wake_trait", issue = "69912")] + #[stable(feature = "wake_trait", since = "1.51.0")] pub use alloc::task::*; } -#[stable(feature = "futures_api", since = "1.36.0")] -pub mod future; - // Platform-abstraction modules #[macro_use] mod sys_common; diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index de072e83df..0ce6542cb7 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -4,11 +4,12 @@ //! library. Each macro is available for use when linking against the standard //! library. +#[cfg(bootstrap)] #[doc(include = "../../core/src/macros/panic.md")] #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(libstd_sys_internals)] -#[cfg_attr(not(any(bootstrap, test)), rustc_diagnostic_item = "std_panic_macro")] +#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")] macro_rules! panic { () => ({ $crate::panic!("explicit panic") }); ($msg:expr $(,)?) => ({ $crate::rt::begin_panic($msg) }); @@ -17,6 +18,21 @@ macro_rules! panic { }); } +#[cfg(not(bootstrap))] +#[doc(include = "../../core/src/macros/panic.md")] +#[macro_export] +#[rustc_builtin_macro = "std_panic"] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow_internal_unstable(edition_panic)] +#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")] +macro_rules! panic { + // Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021` + // depending on the edition of the caller. + ($($arg:tt)*) => { + /* compiler built-in */ + }; +} + /// Prints to the standard output. /// /// Equivalent to the [`println!`] macro except that a newline is not printed at @@ -282,6 +298,10 @@ macro_rules! eprintln { #[macro_export] #[stable(feature = "dbg_macro", since = "1.32.0")] macro_rules! dbg { + // NOTE: We cannot use `concat!` to make a static string as a format argument + // of `eprintln!` because `file!` could contain a `{` or + // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!` + // will be malformed. () => { $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!()); }; diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index d33b772633..2aa305d7f8 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -150,6 +150,7 @@ impl IpAddr { /// ``` #[rustc_const_stable(feature = "const_ip", since = "1.50.0")] #[stable(feature = "ip_shared", since = "1.12.0")] + #[inline] pub const fn is_unspecified(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_unspecified(), @@ -172,6 +173,7 @@ impl IpAddr { /// ``` #[rustc_const_stable(feature = "const_ip", since = "1.50.0")] #[stable(feature = "ip_shared", since = "1.12.0")] + #[inline] pub const fn is_loopback(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_loopback(), @@ -195,6 +197,7 @@ impl IpAddr { /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), true); /// ``` #[rustc_const_unstable(feature = "const_ip", issue = "76205")] + #[inline] pub const fn is_global(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_global(), @@ -217,6 +220,7 @@ impl IpAddr { /// ``` #[rustc_const_stable(feature = "const_ip", since = "1.50.0")] #[stable(feature = "ip_shared", since = "1.12.0")] + #[inline] pub const fn is_multicast(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_multicast(), @@ -243,6 +247,7 @@ impl IpAddr { /// ); /// ``` #[rustc_const_unstable(feature = "const_ip", issue = "76205")] + #[inline] pub const fn is_documentation(&self) -> bool { match self { IpAddr::V4(ip) => ip.is_documentation(), @@ -265,6 +270,7 @@ impl IpAddr { /// ``` #[rustc_const_stable(feature = "const_ip", since = "1.50.0")] #[stable(feature = "ipaddr_checker", since = "1.16.0")] + #[inline] pub const fn is_ipv4(&self) -> bool { matches!(self, IpAddr::V4(_)) } @@ -284,6 +290,7 @@ impl IpAddr { /// ``` #[rustc_const_stable(feature = "const_ip", since = "1.50.0")] #[stable(feature = "ipaddr_checker", since = "1.16.0")] + #[inline] pub const fn is_ipv6(&self) -> bool { matches!(self, IpAddr::V6(_)) } @@ -303,6 +310,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")] #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { // `s_addr` is stored as BE on all machine and the array is in BE order. // So the native endian conversion method is used so that it's never swapped. @@ -360,6 +368,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub const fn octets(&self) -> [u8; 4] { // This returns the order we want because s_addr is stored in big-endian. self.inner.s_addr.to_ne_bytes() @@ -382,6 +391,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")] #[stable(feature = "ip_shared", since = "1.12.0")] + #[inline] pub const fn is_unspecified(&self) -> bool { self.inner.s_addr == 0 } @@ -402,6 +412,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] + #[inline] pub const fn is_loopback(&self) -> bool { self.octets()[0] == 127 } @@ -431,6 +442,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] + #[inline] pub const fn is_private(&self) -> bool { match self.octets() { [10, ..] => true, @@ -457,6 +469,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] + #[inline] pub const fn is_link_local(&self) -> bool { matches!(self.octets(), [169, 254, ..]) } @@ -531,6 +544,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true); /// ``` #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[inline] pub const fn is_global(&self) -> bool { // check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two // globally routable addresses in the 192.0.0.0/24 range. @@ -568,6 +582,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false); /// ``` #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[inline] pub const fn is_shared(&self) -> bool { self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) } @@ -600,6 +615,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(191, 255, 255, 255).is_ietf_protocol_assignment(), false); /// ``` #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[inline] pub const fn is_ietf_protocol_assignment(&self) -> bool { self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0 } @@ -623,6 +639,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false); /// ``` #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[inline] pub const fn is_benchmarking(&self) -> bool { self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 } @@ -655,6 +672,7 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false); /// ``` #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[inline] pub const fn is_reserved(&self) -> bool { self.octets()[0] & 240 == 240 && !self.is_broadcast() } @@ -677,6 +695,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] + #[inline] pub const fn is_multicast(&self) -> bool { self.octets()[0] >= 224 && self.octets()[0] <= 239 } @@ -697,6 +716,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] + #[inline] pub const fn is_broadcast(&self) -> bool { u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets()) } @@ -723,6 +743,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] + #[inline] pub const fn is_documentation(&self) -> bool { match self.octets() { [192, 0, 2, _] => true, @@ -753,6 +774,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); Ipv6Addr { @@ -776,6 +798,7 @@ impl Ipv4Addr { /// ``` #[rustc_const_stable(feature = "const_ipv4", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { let [a, b, c, d] = self.octets(); Ipv6Addr { @@ -817,6 +840,7 @@ impl From for IpAddr { /// IpAddr::from(addr) /// ) /// ``` + #[inline] fn from(ipv4: Ipv4Addr) -> IpAddr { IpAddr::V4(ipv4) } @@ -838,6 +862,7 @@ impl From for IpAddr { /// IpAddr::from(addr) /// ); /// ``` + #[inline] fn from(ipv6: Ipv6Addr) -> IpAddr { IpAddr::V6(ipv6) } @@ -875,6 +900,7 @@ impl fmt::Debug for Ipv4Addr { #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Ipv4Addr { + #[inline] fn clone(&self) -> Ipv4Addr { *self } @@ -882,6 +908,7 @@ impl Clone for Ipv4Addr { #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for Ipv4Addr { + #[inline] fn eq(&self, other: &Ipv4Addr) -> bool { self.inner.s_addr == other.inner.s_addr } @@ -889,6 +916,7 @@ impl PartialEq for Ipv4Addr { #[stable(feature = "ip_cmp", since = "1.16.0")] impl PartialEq for IpAddr { + #[inline] fn eq(&self, other: &Ipv4Addr) -> bool { match self { IpAddr::V4(v4) => v4 == other, @@ -899,6 +927,7 @@ impl PartialEq for IpAddr { #[stable(feature = "ip_cmp", since = "1.16.0")] impl PartialEq for Ipv4Addr { + #[inline] fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(v4) => self == v4, @@ -912,6 +941,7 @@ impl Eq for Ipv4Addr {} #[stable(feature = "rust1", since = "1.0.0")] impl hash::Hash for Ipv4Addr { + #[inline] fn hash(&self, s: &mut H) { // NOTE: // * hash in big endian order @@ -923,6 +953,7 @@ impl hash::Hash for Ipv4Addr { #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for Ipv4Addr { + #[inline] fn partial_cmp(&self, other: &Ipv4Addr) -> Option { Some(self.cmp(other)) } @@ -930,6 +961,7 @@ impl PartialOrd for Ipv4Addr { #[stable(feature = "ip_cmp", since = "1.16.0")] impl PartialOrd for IpAddr { + #[inline] fn partial_cmp(&self, other: &Ipv4Addr) -> Option { match self { IpAddr::V4(v4) => v4.partial_cmp(other), @@ -940,6 +972,7 @@ impl PartialOrd for IpAddr { #[stable(feature = "ip_cmp", since = "1.16.0")] impl PartialOrd for Ipv4Addr { + #[inline] fn partial_cmp(&self, other: &IpAddr) -> Option { match other { IpAddr::V4(v4) => self.partial_cmp(v4), @@ -950,6 +983,7 @@ impl PartialOrd for Ipv4Addr { #[stable(feature = "rust1", since = "1.0.0")] impl Ord for Ipv4Addr { + #[inline] fn cmp(&self, other: &Ipv4Addr) -> Ordering { // Compare as native endian u32::from_be(self.inner.s_addr).cmp(&u32::from_be(other.inner.s_addr)) @@ -974,6 +1008,7 @@ impl From for u32 { /// let addr = Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe); /// assert_eq!(0xcafebabe, u32::from(addr)); /// ``` + #[inline] fn from(ip: Ipv4Addr) -> u32 { let ip = ip.octets(); u32::from_be_bytes(ip) @@ -992,6 +1027,7 @@ impl From for Ipv4Addr { /// let addr = Ipv4Addr::from(0xcafebabe); /// assert_eq!(Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe), addr); /// ``` + #[inline] fn from(ip: u32) -> Ipv4Addr { Ipv4Addr::from(ip.to_be_bytes()) } @@ -1009,6 +1045,7 @@ impl From<[u8; 4]> for Ipv4Addr { /// let addr = Ipv4Addr::from([13u8, 12u8, 11u8, 10u8]); /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); /// ``` + #[inline] fn from(octets: [u8; 4]) -> Ipv4Addr { Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]) } @@ -1026,6 +1063,7 @@ impl From<[u8; 4]> for IpAddr { /// let addr = IpAddr::from([13u8, 12u8, 11u8, 10u8]); /// assert_eq!(IpAddr::V4(Ipv4Addr::new(13, 12, 11, 10)), addr); /// ``` + #[inline] fn from(octets: [u8; 4]) -> IpAddr { IpAddr::V4(Ipv4Addr::from(octets)) } @@ -1046,6 +1084,7 @@ impl Ipv6Addr { #[rustc_allow_const_fn_unstable(const_fn_transmute)] #[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")] #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { let addr16 = [ a.to_be(), @@ -1107,6 +1146,7 @@ impl Ipv6Addr { #[rustc_allow_const_fn_unstable(const_fn_transmute)] #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub const fn segments(&self) -> [u16; 8] { // All elements in `s6_addr` must be big endian. // SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`. @@ -1142,6 +1182,7 @@ impl Ipv6Addr { /// ``` #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] + #[inline] pub const fn is_unspecified(&self) -> bool { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) } @@ -1162,6 +1203,7 @@ impl Ipv6Addr { /// ``` #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] + #[inline] pub const fn is_loopback(&self) -> bool { u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) } @@ -1186,6 +1228,7 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1).is_global(), true); /// ``` #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[inline] pub const fn is_global(&self) -> bool { match self.multicast_scope() { Some(Ipv6MulticastScope::Global) => true, @@ -1211,14 +1254,15 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true); /// ``` #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[inline] pub const fn is_unique_local(&self) -> bool { (self.segments()[0] & 0xfe00) == 0xfc00 } /// Returns [`true`] if the address is a unicast link-local address (`fe80::/64`). /// - /// A common mis-conception is to think that "unicast link-local addresses start with - /// `fe80::`", but the [IETF RFC 4291] actually defines a stricter format for these addresses: + /// A common misconception is to think that "unicast link-local addresses start with + /// `fe80::`", but [IETF RFC 4291] actually defines a stricter format for these addresses: /// /// ```no_rust /// | 10 | @@ -1228,9 +1272,9 @@ impl Ipv6Addr { /// +----------+-------------------------+----------------------------+ /// ``` /// - /// This method validates the format defined in the RFC and won't recognize the following - /// addresses such as `fe80:0:0:1::` or `fe81::` as unicast link-local addresses for example. - /// If you need a less strict validation use [`Ipv6Addr::is_unicast_link_local()`] instead. + /// This method validates the format defined in the RFC and won't recognize addresses + /// like `fe80:0:0:1::` or `fe81::` as unicast link-local addresses. + /// If you need a less strict validation, use [`Ipv6Addr::is_unicast_link_local()`] instead. /// /// # Examples /// @@ -1265,6 +1309,7 @@ impl Ipv6Addr { /// [IETF RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[inline] pub const fn is_unicast_link_local_strict(&self) -> bool { matches!(self.segments(), [0xfe80, 0, 0, 0, ..]) } @@ -1282,7 +1327,7 @@ impl Ipv6Addr { /// +----------+-------------------------+----------------------------+ /// ``` /// - /// As a result, this method consider addresses such as `fe80:0:0:1::` or `fe81::` to be + /// As a result, this method considers addresses such as `fe80:0:0:1::` or `fe81::` to be /// unicast link-local addresses, whereas [`Ipv6Addr::is_unicast_link_local_strict()`] does not. /// If you need a strict validation fully compliant with the RFC, use /// [`Ipv6Addr::is_unicast_link_local_strict()`] instead. @@ -1318,6 +1363,7 @@ impl Ipv6Addr { /// [IETF RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[inline] pub const fn is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } @@ -1357,12 +1403,13 @@ impl Ipv6Addr { /// /// [RFC 3879]: https://tools.ietf.org/html/rfc3879 #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[inline] pub const fn is_unicast_site_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfec0 } /// Returns [`true`] if this is an address reserved for documentation - /// (2001:db8::/32). + /// (`2001:db8::/32`). /// /// This property is defined in [IETF RFC 3849]. /// @@ -1379,6 +1426,7 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); /// ``` #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[inline] pub const fn is_documentation(&self) -> bool { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) } @@ -1414,6 +1462,7 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), true); /// ``` #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[inline] pub const fn is_unicast_global(&self) -> bool { !self.is_multicast() && !self.is_loopback() @@ -1439,6 +1488,7 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None); /// ``` #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[inline] pub const fn multicast_scope(&self) -> Option { if self.is_multicast() { match self.segments()[0] & 0x000f { @@ -1472,6 +1522,7 @@ impl Ipv6Addr { /// ``` #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(since = "1.7.0", feature = "ip_17")] + #[inline] pub const fn is_multicast(&self) -> bool { (self.segments()[0] & 0xff00) == 0xff00 } @@ -1498,6 +1549,7 @@ impl Ipv6Addr { /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None); /// ``` #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[inline] pub const fn to_ipv4_mapped(&self) -> Option { match self.octets() { [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { @@ -1527,6 +1579,7 @@ impl Ipv6Addr { /// ``` #[rustc_const_stable(feature = "const_ipv6", since = "1.50.0")] #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub const fn to_ipv4(&self) -> Option { if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { let [a, b] = ab.to_be_bytes(); @@ -1547,6 +1600,7 @@ impl Ipv6Addr { /// ``` #[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")] #[stable(feature = "ipv6_to_octets", since = "1.12.0")] + #[inline] pub const fn octets(&self) -> [u8; 16] { self.inner.s6_addr } @@ -1610,11 +1664,11 @@ impl fmt::Display for Ipv6Addr { /// Write a colon-separated part of the address #[inline] fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result { - if let Some(first) = chunk.first() { - fmt::LowerHex::fmt(first, f)?; - for segment in &chunk[1..] { + if let Some((first, tail)) = chunk.split_first() { + write!(f, "{:x}", first)?; + for segment in tail { f.write_char(':')?; - fmt::LowerHex::fmt(segment, f)?; + write!(f, "{:x}", segment)?; } } Ok(()) @@ -1658,6 +1712,7 @@ impl fmt::Debug for Ipv6Addr { #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Ipv6Addr { + #[inline] fn clone(&self) -> Ipv6Addr { *self } @@ -1665,6 +1720,7 @@ impl Clone for Ipv6Addr { #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for Ipv6Addr { + #[inline] fn eq(&self, other: &Ipv6Addr) -> bool { self.inner.s6_addr == other.inner.s6_addr } @@ -1672,6 +1728,7 @@ impl PartialEq for Ipv6Addr { #[stable(feature = "ip_cmp", since = "1.16.0")] impl PartialEq for Ipv6Addr { + #[inline] fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(_) => false, @@ -1682,6 +1739,7 @@ impl PartialEq for Ipv6Addr { #[stable(feature = "ip_cmp", since = "1.16.0")] impl PartialEq for IpAddr { + #[inline] fn eq(&self, other: &Ipv6Addr) -> bool { match self { IpAddr::V4(_) => false, @@ -1695,6 +1753,7 @@ impl Eq for Ipv6Addr {} #[stable(feature = "rust1", since = "1.0.0")] impl hash::Hash for Ipv6Addr { + #[inline] fn hash(&self, s: &mut H) { self.inner.s6_addr.hash(s) } @@ -1702,6 +1761,7 @@ impl hash::Hash for Ipv6Addr { #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for Ipv6Addr { + #[inline] fn partial_cmp(&self, other: &Ipv6Addr) -> Option { Some(self.cmp(other)) } @@ -1709,6 +1769,7 @@ impl PartialOrd for Ipv6Addr { #[stable(feature = "ip_cmp", since = "1.16.0")] impl PartialOrd for IpAddr { + #[inline] fn partial_cmp(&self, other: &Ipv6Addr) -> Option { match self { IpAddr::V4(_) => Some(Ordering::Less), @@ -1719,6 +1780,7 @@ impl PartialOrd for IpAddr { #[stable(feature = "ip_cmp", since = "1.16.0")] impl PartialOrd for Ipv6Addr { + #[inline] fn partial_cmp(&self, other: &IpAddr) -> Option { match other { IpAddr::V4(_) => Some(Ordering::Greater), @@ -1729,6 +1791,7 @@ impl PartialOrd for Ipv6Addr { #[stable(feature = "rust1", since = "1.0.0")] impl Ord for Ipv6Addr { + #[inline] fn cmp(&self, other: &Ipv6Addr) -> Ordering { self.segments().cmp(&other.segments()) } @@ -1760,6 +1823,7 @@ impl From for u128 { /// ); /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr)); /// ``` + #[inline] fn from(ip: Ipv6Addr) -> u128 { let ip = ip.octets(); u128::from_be_bytes(ip) @@ -1782,6 +1846,7 @@ impl From for Ipv6Addr { /// ), /// addr); /// ``` + #[inline] fn from(ip: u128) -> Ipv6Addr { Ipv6Addr::from(ip.to_be_bytes()) } @@ -1810,6 +1875,7 @@ impl From<[u8; 16]> for Ipv6Addr { /// addr /// ); /// ``` + #[inline] fn from(octets: [u8; 16]) -> Ipv6Addr { let inner = c::in6_addr { s6_addr: octets }; Ipv6Addr::from_inner(inner) @@ -1839,6 +1905,7 @@ impl From<[u16; 8]> for Ipv6Addr { /// addr /// ); /// ``` + #[inline] fn from(segments: [u16; 8]) -> Ipv6Addr { let [a, b, c, d, e, f, g, h] = segments; Ipv6Addr::new(a, b, c, d, e, f, g, h) @@ -1868,6 +1935,7 @@ impl From<[u8; 16]> for IpAddr { /// addr /// ); /// ``` + #[inline] fn from(octets: [u8; 16]) -> IpAddr { IpAddr::V6(Ipv6Addr::from(octets)) } @@ -1896,6 +1964,7 @@ impl From<[u16; 8]> for IpAddr { /// addr /// ); /// ``` + #[inline] fn from(segments: [u16; 8]) -> IpAddr { IpAddr::V6(Ipv6Addr::from(segments)) } diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs index 44fb3adf07..ef0d4edc43 100644 --- a/library/std/src/net/ip/tests.rs +++ b/library/std/src/net/ip/tests.rs @@ -166,6 +166,9 @@ fn ipv6_addr_to_string() { // two runs of zeros, equal length assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string()); + + // don't prefix `0x` to each segment in `dbg!`. + assert_eq!("1::4:5:0:0:8", &format!("{:#?}", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8))); } #[test] diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index 617c4098aa..525102212c 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -247,17 +247,17 @@ mod arch { use crate::os::raw::{c_int, c_long}; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; + pub type blkcnt_t = i64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; + pub type blksize_t = i32; #[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; + pub type nlink_t = u32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; + pub type off_t = i64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; + pub type time_t = c_long; #[repr(C)] #[derive(Clone)] @@ -288,15 +288,15 @@ mod arch { #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blocks: i64, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: i64, + pub st_atime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_atime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: i64, + pub st_mtime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mtime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: i64, + pub st_ctime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ctime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index d18b94b6c1..89a822a722 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -12,11 +12,33 @@ use crate::panicking; use crate::pin::Pin; use crate::ptr::{NonNull, Unique}; use crate::rc::Rc; +use crate::stream::Stream; use crate::sync::atomic; use crate::sync::{Arc, Mutex, RwLock}; use crate::task::{Context, Poll}; use crate::thread::Result; +#[doc(hidden)] +#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] +#[allow_internal_unstable(libstd_sys_internals)] +#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")] +#[rustc_macro_transparency = "semitransparent"] +pub macro panic_2015 { + () => ({ + $crate::rt::begin_panic("explicit panic") + }), + ($msg:expr $(,)?) => ({ + $crate::rt::begin_panic($msg) + }), + ($fmt:expr, $($arg:tt)+) => ({ + $crate::rt::begin_panic_fmt(&$crate::format_args!($fmt, $($arg)+)) + }), +} + +#[doc(hidden)] +#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] +pub use core::panic::panic_2021; + #[stable(feature = "panic_hooks", since = "1.10.0")] pub use crate::panicking::{set_hook, take_hook}; @@ -31,9 +53,9 @@ pub use core::panic::{Location, PanicInfo}; /// accessed later using [`PanicInfo::payload`]. /// /// See the [`panic!`] macro for more information about panicking. -#[unstable(feature = "panic_any", issue = "78500")] +#[stable(feature = "panic_any", since = "1.51.0")] #[inline] -pub fn panic_any(msg: M) -> ! { +pub fn panic_any(msg: M) -> ! { crate::panicking::begin_panic(msg); } @@ -340,6 +362,19 @@ impl Future for AssertUnwindSafe { } } +#[unstable(feature = "async_stream", issue = "79024")] +impl Stream for AssertUnwindSafe { + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { self.map_unchecked_mut(|x| &mut x.0) }.poll_next(cx) + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + /// Invokes a closure, capturing the cause of an unwinding panic if one occurs. /// /// This function will return `Ok` with the closure's result if the closure diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 8ba3feccb6..6cd572cbe8 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -44,11 +44,11 @@ use realstd::io::set_output_capture; extern "C" { fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static); - /// `payload` is actually a `*mut &mut dyn BoxMeUp` but that would cause FFI warnings. - /// It cannot be `Box` because the other end of this call does not depend - /// on liballoc, and thus cannot use `Box`. + /// `payload` is passed through another layer of raw pointers as `&mut dyn Trait` is not + /// FFI-safe. `BoxMeUp` lazily performs allocation only when needed (this avoids allocations + /// when using the "abort" panic runtime). #[unwind(allowed)] - fn __rust_start_panic(payload: usize) -> u32; + fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32; } /// This function is called by the panic runtime if FFI code catches a Rust @@ -637,7 +637,7 @@ pub fn rust_panic_without_hook(payload: Box) -> ! { fn rust_panic(mut msg: &mut dyn BoxMeUp) -> ! { let code = unsafe { let obj = &mut msg as *mut &mut dyn BoxMeUp; - __rust_start_panic(obj as usize) + __rust_start_panic(obj) }; rtabort!("failed to initiate panic, error {}", code) } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 8a75c1d605..1889e54933 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -401,12 +401,14 @@ impl<'a> PrefixComponent<'a> { /// See [`Prefix`]'s documentation for more information on the different /// kinds of prefixes. #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn kind(&self) -> Prefix<'a> { self.parsed } /// Returns the raw [`OsStr`] slice for this prefix. #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn as_os_str(&self) -> &'a OsStr { self.raw } @@ -414,6 +416,7 @@ impl<'a> PrefixComponent<'a> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a> cmp::PartialEq for PrefixComponent<'a> { + #[inline] fn eq(&self, other: &PrefixComponent<'a>) -> bool { cmp::PartialEq::eq(&self.parsed, &other.parsed) } @@ -421,6 +424,7 @@ impl<'a> cmp::PartialEq for PrefixComponent<'a> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a> cmp::PartialOrd for PrefixComponent<'a> { + #[inline] fn partial_cmp(&self, other: &PrefixComponent<'a>) -> Option { cmp::PartialOrd::partial_cmp(&self.parsed, &other.parsed) } @@ -428,6 +432,7 @@ impl<'a> cmp::PartialOrd for PrefixComponent<'a> { #[stable(feature = "rust1", since = "1.0.0")] impl cmp::Ord for PrefixComponent<'_> { + #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { cmp::Ord::cmp(&self.parsed, &other.parsed) } @@ -522,6 +527,7 @@ impl<'a> Component<'a> { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for Component<'_> { + #[inline] fn as_ref(&self) -> &OsStr { self.as_os_str() } @@ -529,6 +535,7 @@ impl AsRef for Component<'_> { #[stable(feature = "path_component_asref", since = "1.25.0")] impl AsRef for Component<'_> { + #[inline] fn as_ref(&self) -> &Path { self.as_os_str().as_ref() } @@ -750,6 +757,7 @@ impl<'a> Components<'a> { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for Components<'_> { + #[inline] fn as_ref(&self) -> &Path { self.as_path() } @@ -757,6 +765,7 @@ impl AsRef for Components<'_> { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for Components<'_> { + #[inline] fn as_ref(&self) -> &OsStr { self.as_path().as_os_str() } @@ -792,6 +801,7 @@ impl<'a> Iter<'a> { /// assert_eq!(Path::new("foo/bar.txt"), iter.as_path()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn as_path(&self) -> &'a Path { self.inner.as_path() } @@ -799,6 +809,7 @@ impl<'a> Iter<'a> { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for Iter<'_> { + #[inline] fn as_ref(&self) -> &Path { self.as_path() } @@ -806,6 +817,7 @@ impl AsRef for Iter<'_> { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for Iter<'_> { + #[inline] fn as_ref(&self) -> &OsStr { self.as_path().as_os_str() } @@ -815,6 +827,7 @@ impl AsRef for Iter<'_> { impl<'a> Iterator for Iter<'a> { type Item = &'a OsStr; + #[inline] fn next(&mut self) -> Option<&'a OsStr> { self.inner.next().map(Component::as_os_str) } @@ -822,6 +835,7 @@ impl<'a> Iterator for Iter<'a> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a> DoubleEndedIterator for Iter<'a> { + #[inline] fn next_back(&mut self) -> Option<&'a OsStr> { self.inner.next_back().map(Component::as_os_str) } @@ -935,6 +949,7 @@ impl FusedIterator for Components<'_> {} #[stable(feature = "rust1", since = "1.0.0")] impl<'a> cmp::PartialEq for Components<'a> { + #[inline] fn eq(&self, other: &Components<'a>) -> bool { Iterator::eq(self.clone(), other.clone()) } @@ -945,6 +960,7 @@ impl cmp::Eq for Components<'_> {} #[stable(feature = "rust1", since = "1.0.0")] impl<'a> cmp::PartialOrd for Components<'a> { + #[inline] fn partial_cmp(&self, other: &Components<'a>) -> Option { Iterator::partial_cmp(self.clone(), other.clone()) } @@ -952,6 +968,7 @@ impl<'a> cmp::PartialOrd for Components<'a> { #[stable(feature = "rust1", since = "1.0.0")] impl cmp::Ord for Components<'_> { + #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { Iterator::cmp(self.clone(), other.clone()) } @@ -985,6 +1002,7 @@ pub struct Ancestors<'a> { impl<'a> Iterator for Ancestors<'a> { type Item = &'a Path; + #[inline] fn next(&mut self) -> Option { let next = self.next; self.next = next.and_then(Path::parent); @@ -1060,6 +1078,7 @@ pub struct PathBuf { } impl PathBuf { + #[inline] fn as_mut_vec(&mut self) -> &mut Vec { unsafe { &mut *(self as *mut PathBuf as *mut Vec) } } @@ -1074,6 +1093,7 @@ impl PathBuf { /// let path = PathBuf::new(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn new() -> PathBuf { PathBuf { inner: OsString::new() } } @@ -1097,6 +1117,7 @@ impl PathBuf { /// /// [`with_capacity`]: OsString::with_capacity #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[inline] pub fn with_capacity(capacity: usize) -> PathBuf { PathBuf { inner: OsString::with_capacity(capacity) } } @@ -1112,6 +1133,7 @@ impl PathBuf { /// assert_eq!(Path::new("/test"), p.as_path()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn as_path(&self) -> &Path { self } @@ -1315,12 +1337,14 @@ impl PathBuf { /// let os_str = p.into_os_string(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn into_os_string(self) -> OsString { self.inner } /// Converts this `PathBuf` into a [boxed](Box) [`Path`]. #[stable(feature = "into_boxed_path", since = "1.20.0")] + #[inline] pub fn into_boxed_path(self) -> Box { let rw = Box::into_raw(self.inner.into_boxed_os_str()) as *mut Path; unsafe { Box::from_raw(rw) } @@ -1330,6 +1354,7 @@ impl PathBuf { /// /// [`capacity`]: OsString::capacity #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[inline] pub fn capacity(&self) -> usize { self.inner.capacity() } @@ -1338,6 +1363,7 @@ impl PathBuf { /// /// [`clear`]: OsString::clear #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[inline] pub fn clear(&mut self) { self.inner.clear() } @@ -1346,6 +1372,7 @@ impl PathBuf { /// /// [`reserve`]: OsString::reserve #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[inline] pub fn reserve(&mut self, additional: usize) { self.inner.reserve(additional) } @@ -1354,6 +1381,7 @@ impl PathBuf { /// /// [`reserve_exact`]: OsString::reserve_exact #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[inline] pub fn reserve_exact(&mut self, additional: usize) { self.inner.reserve_exact(additional) } @@ -1362,6 +1390,7 @@ impl PathBuf { /// /// [`shrink_to_fit`]: OsString::shrink_to_fit #[stable(feature = "path_buf_capacity", since = "1.44.0")] + #[inline] pub fn shrink_to_fit(&mut self) { self.inner.shrink_to_fit() } @@ -1370,6 +1399,7 @@ impl PathBuf { /// /// [`shrink_to`]: OsString::shrink_to #[unstable(feature = "shrink_to", issue = "56431")] + #[inline] pub fn shrink_to(&mut self, min_capacity: usize) { self.inner.shrink_to(min_capacity) } @@ -1400,6 +1430,7 @@ impl From> for PathBuf { /// Converts a `Box` into a `PathBuf` /// /// This conversion does not allocate or copy memory. + #[inline] fn from(boxed: Box) -> PathBuf { boxed.into_path_buf() } @@ -1411,6 +1442,7 @@ impl From for Box { /// /// This conversion currently should not allocate memory, /// but this behavior is not guaranteed on all platforms or in all future versions. + #[inline] fn from(p: PathBuf) -> Box { p.into_boxed_path() } @@ -1426,6 +1458,7 @@ impl Clone for Box { #[stable(feature = "rust1", since = "1.0.0")] impl> From<&T> for PathBuf { + #[inline] fn from(s: &T) -> PathBuf { PathBuf::from(s.as_ref().to_os_string()) } @@ -1447,6 +1480,7 @@ impl From for OsString { /// Converts a `PathBuf` into a `OsString` /// /// This conversion does not allocate or copy memory. + #[inline] fn from(path_buf: PathBuf) -> OsString { path_buf.inner } @@ -1457,6 +1491,7 @@ impl From for PathBuf { /// Converts a `String` into a `PathBuf` /// /// This conversion does not allocate or copy memory. + #[inline] fn from(s: String) -> PathBuf { PathBuf::from(OsString::from(s)) } @@ -1466,6 +1501,7 @@ impl From for PathBuf { impl FromStr for PathBuf { type Err = core::convert::Infallible; + #[inline] fn from_str(s: &str) -> Result { Ok(PathBuf::from(s)) } @@ -1510,6 +1546,7 @@ impl ops::Deref for PathBuf { #[stable(feature = "rust1", since = "1.0.0")] impl Borrow for PathBuf { + #[inline] fn borrow(&self) -> &Path { self.deref() } @@ -1517,6 +1554,7 @@ impl Borrow for PathBuf { #[stable(feature = "default_for_pathbuf", since = "1.17.0")] impl Default for PathBuf { + #[inline] fn default() -> Self { PathBuf::new() } @@ -1597,9 +1635,11 @@ impl From<&Path> for Rc { #[stable(feature = "rust1", since = "1.0.0")] impl ToOwned for Path { type Owned = PathBuf; + #[inline] fn to_owned(&self) -> PathBuf { self.to_path_buf() } + #[inline] fn clone_into(&self, target: &mut PathBuf) { self.inner.clone_into(&mut target.inner); } @@ -1607,6 +1647,7 @@ impl ToOwned for Path { #[stable(feature = "rust1", since = "1.0.0")] impl cmp::PartialEq for PathBuf { + #[inline] fn eq(&self, other: &PathBuf) -> bool { self.components() == other.components() } @@ -1624,6 +1665,7 @@ impl cmp::Eq for PathBuf {} #[stable(feature = "rust1", since = "1.0.0")] impl cmp::PartialOrd for PathBuf { + #[inline] fn partial_cmp(&self, other: &PathBuf) -> Option { self.components().partial_cmp(other.components()) } @@ -1631,6 +1673,7 @@ impl cmp::PartialOrd for PathBuf { #[stable(feature = "rust1", since = "1.0.0")] impl cmp::Ord for PathBuf { + #[inline] fn cmp(&self, other: &PathBuf) -> cmp::Ordering { self.components().cmp(other.components()) } @@ -1638,6 +1681,7 @@ impl cmp::Ord for PathBuf { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for PathBuf { + #[inline] fn as_ref(&self) -> &OsStr { &self.inner[..] } @@ -1745,6 +1789,7 @@ impl Path { /// assert_eq!(os_str, std::ffi::OsStr::new("foo.txt")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn as_os_str(&self) -> &OsStr { &self.inner } @@ -1766,6 +1811,7 @@ impl Path { /// assert_eq!(path.to_str(), Some("foo.txt")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn to_str(&self) -> Option<&str> { self.inner.to_str() } @@ -1775,7 +1821,6 @@ impl Path { /// Any non-Unicode sequences are replaced with /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. /// - /// [`Cow`]: Cow /// [U+FFFD]: super::char::REPLACEMENT_CHARACTER /// /// # Examples @@ -1792,6 +1837,7 @@ impl Path { /// Had `path` contained invalid unicode, the `to_string_lossy` call might /// have returned `"fo�.txt"`. #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn to_string_lossy(&self) -> Cow<'_, str> { self.inner.to_string_lossy() } @@ -1855,6 +1901,7 @@ impl Path { /// /// [`is_absolute`]: Path::is_absolute #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn is_relative(&self) -> bool { !self.is_absolute() } @@ -1880,6 +1927,7 @@ impl Path { /// assert!(Path::new("/etc/passwd").has_root()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn has_root(&self) -> bool { self.components().has_root() } @@ -1942,6 +1990,7 @@ impl Path { /// /// [`parent`]: Path::parent #[stable(feature = "path_ancestors", since = "1.28.0")] + #[inline] pub fn ancestors(&self) -> Ancestors<'_> { Ancestors { next: Some(&self) } } @@ -2266,6 +2315,7 @@ impl Path { /// assert_eq!(it.next(), None) /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn iter(&self) -> Iter<'_> { Iter { inner: self.components() } } @@ -2285,6 +2335,7 @@ impl Path { /// println!("{}", path.display()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn display(&self) -> Display<'_> { Display { path: self } } @@ -2306,6 +2357,7 @@ impl Path { /// println!("{:?}", metadata.file_type()); /// ``` #[stable(feature = "path_ext", since = "1.5.0")] + #[inline] pub fn metadata(&self) -> io::Result { fs::metadata(self) } @@ -2324,6 +2376,7 @@ impl Path { /// println!("{:?}", metadata.file_type()); /// ``` #[stable(feature = "path_ext", since = "1.5.0")] + #[inline] pub fn symlink_metadata(&self) -> io::Result { fs::symlink_metadata(self) } @@ -2342,6 +2395,7 @@ impl Path { /// assert_eq!(path.canonicalize().unwrap(), PathBuf::from("/foo/test/bar.rs")); /// ``` #[stable(feature = "path_ext", since = "1.5.0")] + #[inline] pub fn canonicalize(&self) -> io::Result { fs::canonicalize(self) } @@ -2359,6 +2413,7 @@ impl Path { /// let path_link = path.read_link().expect("read_link call failed"); /// ``` #[stable(feature = "path_ext", since = "1.5.0")] + #[inline] pub fn read_link(&self) -> io::Result { fs::read_link(self) } @@ -2383,6 +2438,7 @@ impl Path { /// } /// ``` #[stable(feature = "path_ext", since = "1.5.0")] + #[inline] pub fn read_dir(&self) -> io::Result { fs::read_dir(self) } @@ -2407,6 +2463,7 @@ impl Path { /// This is a convenience function that coerces errors to false. If you want to /// check errors, call [`fs::metadata`]. #[stable(feature = "path_ext", since = "1.5.0")] + #[inline] pub fn exists(&self) -> bool { fs::metadata(self).is_ok() } @@ -2481,6 +2538,7 @@ impl Path { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for Path { + #[inline] fn as_ref(&self) -> &OsStr { &self.inner } @@ -2532,6 +2590,7 @@ impl fmt::Display for Display<'_> { #[stable(feature = "rust1", since = "1.0.0")] impl cmp::PartialEq for Path { + #[inline] fn eq(&self, other: &Path) -> bool { self.components().eq(other.components()) } @@ -2551,6 +2610,7 @@ impl cmp::Eq for Path {} #[stable(feature = "rust1", since = "1.0.0")] impl cmp::PartialOrd for Path { + #[inline] fn partial_cmp(&self, other: &Path) -> Option { self.components().partial_cmp(other.components()) } @@ -2558,6 +2618,7 @@ impl cmp::PartialOrd for Path { #[stable(feature = "rust1", since = "1.0.0")] impl cmp::Ord for Path { + #[inline] fn cmp(&self, other: &Path) -> cmp::Ordering { self.components().cmp(other.components()) } @@ -2565,6 +2626,7 @@ impl cmp::Ord for Path { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for Path { + #[inline] fn as_ref(&self) -> &Path { self } @@ -2572,6 +2634,7 @@ impl AsRef for Path { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for OsStr { + #[inline] fn as_ref(&self) -> &Path { Path::new(self) } @@ -2579,6 +2642,7 @@ impl AsRef for OsStr { #[stable(feature = "cow_os_str_as_ref_path", since = "1.8.0")] impl AsRef for Cow<'_, OsStr> { + #[inline] fn as_ref(&self) -> &Path { Path::new(self) } @@ -2586,6 +2650,7 @@ impl AsRef for Cow<'_, OsStr> { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for OsString { + #[inline] fn as_ref(&self) -> &Path { Path::new(self) } @@ -2601,6 +2666,7 @@ impl AsRef for str { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for String { + #[inline] fn as_ref(&self) -> &Path { Path::new(self) } @@ -2618,6 +2684,7 @@ impl AsRef for PathBuf { impl<'a> IntoIterator for &'a PathBuf { type Item = &'a OsStr; type IntoIter = Iter<'a>; + #[inline] fn into_iter(self) -> Iter<'a> { self.iter() } @@ -2627,6 +2694,7 @@ impl<'a> IntoIterator for &'a PathBuf { impl<'a> IntoIterator for &'a Path { type Item = &'a OsStr; type IntoIter = Iter<'a>; + #[inline] fn into_iter(self) -> Iter<'a> { self.iter() } diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index a3776681d0..eb2095b819 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -1,4 +1,4 @@ -//! The Rust Prelude. +//! # The Rust Prelude //! //! Rust comes with a variety of things in its standard library. However, if //! you had to manually import every single thing that you used, it would be @@ -28,35 +28,35 @@ //! The current version of the prelude (version 1) lives in //! [`std::prelude::v1`], and re-exports the following: //! -//! * [`std::marker`]::{[`Copy`], [`Send`], [`Sized`], [`Sync`], [`Unpin`]}, +//! * [`std::marker`]::{[`Copy`], [`Send`], [`Sized`], [`Sync`], [`Unpin`]}: //! marker traits that indicate fundamental properties of types. -//! * [`std::ops`]::{[`Drop`], [`Fn`], [`FnMut`], [`FnOnce`]}, various +//! * [`std::ops`]::{[`Drop`], [`Fn`], [`FnMut`], [`FnOnce`]}: various //! operations for both destructors and overloading `()`. -//! * [`std::mem`]::[`drop`][`mem::drop`], a convenience function for explicitly +//! * [`std::mem`]::[`drop`][`mem::drop`]: a convenience function for explicitly //! dropping a value. -//! * [`std::boxed`]::[`Box`], a way to allocate values on the heap. -//! * [`std::borrow`]::[`ToOwned`], the conversion trait that defines +//! * [`std::boxed`]::[`Box`]: a way to allocate values on the heap. +//! * [`std::borrow`]::[`ToOwned`]: the conversion trait that defines //! [`to_owned`], the generic method for creating an owned type from a //! borrowed type. -//! * [`std::clone`]::[`Clone`], the ubiquitous trait that defines +//! * [`std::clone`]::[`Clone`]: the ubiquitous trait that defines //! [`clone`][`Clone::clone`], the method for producing a copy of a value. -//! * [`std::cmp`]::{[`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`] }, the +//! * [`std::cmp`]::{[`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]}: the //! comparison traits, which implement the comparison operators and are often //! seen in trait bounds. -//! * [`std::convert`]::{[`AsRef`], [`AsMut`], [`Into`], [`From`]}, generic +//! * [`std::convert`]::{[`AsRef`], [`AsMut`], [`Into`], [`From`]}: generic //! conversions, used by savvy API authors to create overloaded methods. //! * [`std::default`]::[`Default`], types that have default values. -//! * [`std::iter`]::{[`Iterator`], [`Extend`], [`IntoIterator`] -//! [`DoubleEndedIterator`], [`ExactSizeIterator`]}, iterators of various +//! * [`std::iter`]::{[`Iterator`], [`Extend`], [`IntoIterator`], +//! [`DoubleEndedIterator`], [`ExactSizeIterator`]}: iterators of various //! kinds. //! * [`std::option`]::[`Option`]::{[`self`][`Option`], [`Some`], [`None`]}, a //! type which expresses the presence or absence of a value. This type is so //! commonly used, its variants are also exported. -//! * [`std::result`]::[`Result`]::{[`self`][`Result`], [`Ok`], [`Err`]}, a type +//! * [`std::result`]::[`Result`]::{[`self`][`Result`], [`Ok`], [`Err`]}: a type //! for functions that may succeed or fail. Like [`Option`], its variants are //! exported as well. -//! * [`std::string`]::{[`String`], [`ToString`]}, heap allocated strings. -//! * [`std::vec`]::[`Vec`], a growable, heap-allocated vector. +//! * [`std::string`]::{[`String`], [`ToString`]}: heap-allocated strings. +//! * [`std::vec`]::[`Vec`]: a growable, heap-allocated vector. //! //! [`mem::drop`]: crate::mem::drop //! [`std::borrow`]: crate::borrow diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 0fbd6b62f1..26302d0ecf 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -41,17 +41,17 @@ pub use crate::result::Result::{self, Err, Ok}; pub use core::prelude::v1::{ asm, assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, format_args_nl, global_asm, include, include_bytes, include_str, line, llvm_asm, log_syntax, - module_path, option_env, stringify, trace_macros, + module_path, option_env, stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, + PartialEq, PartialOrd, }; -// FIXME: Attribute and derive macros are not documented because for them rustdoc generates +// FIXME: Attribute and internal derive macros are not documented because for them rustdoc generates // dead links which fail link checker testing. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] #[doc(hidden)] pub use core::prelude::v1::{ - bench, global_allocator, test, test_case, Clone, Copy, Debug, Default, Eq, Hash, Ord, - PartialEq, PartialOrd, RustcDecodable, RustcEncodable, + bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable, }; #[unstable( diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index ec12e9f09d..876b2b8a3f 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -11,8 +11,9 @@ /// `bool` implements various traits, such as [`BitAnd`], [`BitOr`], [`Not`], etc., /// which allow us to perform boolean operations using `&`, `|` and `!`. /// -/// `if` always demands a `bool` value. [`assert!`], which is an important macro in testing, -/// checks whether an expression returns `true` and panics if it isn't. +/// `if` requires a `bool` value as its conditional. [`assert!`], which is an +/// important macro in testing, checks whether an expression is `true` and panics +/// if it isn't. /// /// ``` /// let bool_val = true & false | false; @@ -25,7 +26,7 @@ /// /// # Examples /// -/// A trivial example of the usage of `bool`, +/// A trivial example of the usage of `bool`: /// /// ``` /// let praise_the_borrow_checker = true; @@ -122,9 +123,9 @@ mod prim_bool {} /// `!`, if we have to call [`String::from_str`] for some reason the result will be a /// [`Result`] which we can unpack like this: /// -/// ```ignore (string-from-str-error-type-is-not-never-yet) -/// #[feature(exhaustive_patterns)] -/// // NOTE: this does not work today! +/// ``` +/// #![feature(exhaustive_patterns)] +/// use std::str::FromStr; /// let Ok(s) = String::from_str("hello"); /// ``` /// @@ -184,9 +185,6 @@ mod prim_bool {} /// because `!` coerces to `Result` automatically. /// /// [`String::from_str`]: str::FromStr::from_str -/// [`Result`]: Result -/// [`Result`]: Result -/// [`Result`]: Result /// [`String`]: string::String /// [`FromStr`]: str::FromStr /// diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 6a33083448..2e5f843fc4 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -125,7 +125,7 @@ unsafe impl Send for Once {} /// State yielded to [`Once::call_once_force()`]’s closure parameter. The state /// can be used to query the poison status of the [`Once`]. -#[unstable(feature = "once_poison", issue = "33577")] +#[stable(feature = "once_poison", since = "1.51.0")] #[derive(Debug)] pub struct OnceState { poisoned: bool, @@ -280,8 +280,6 @@ impl Once { /// # Examples /// /// ``` - /// #![feature(once_poison)] - /// /// use std::sync::Once; /// use std::thread; /// @@ -301,13 +299,13 @@ impl Once { /// /// // call_once_force will still run and reset the poisoned state /// INIT.call_once_force(|state| { - /// assert!(state.poisoned()); + /// assert!(state.is_poisoned()); /// }); /// /// // once any success happens, we stop propagating the poison /// INIT.call_once(|| {}); /// ``` - #[unstable(feature = "once_poison", issue = "33577")] + #[stable(feature = "once_poison", since = "1.51.0")] pub fn call_once_force(&self, f: F) where F: FnOnce(&OnceState), @@ -526,8 +524,6 @@ impl OnceState { /// A poisoned [`Once`]: /// /// ``` - /// #![feature(once_poison)] - /// /// use std::sync::Once; /// use std::thread; /// @@ -540,24 +536,22 @@ impl OnceState { /// assert!(handle.join().is_err()); /// /// INIT.call_once_force(|state| { - /// assert!(state.poisoned()); + /// assert!(state.is_poisoned()); /// }); /// ``` /// /// An unpoisoned [`Once`]: /// /// ``` - /// #![feature(once_poison)] - /// /// use std::sync::Once; /// /// static INIT: Once = Once::new(); /// /// INIT.call_once_force(|state| { - /// assert!(!state.poisoned()); + /// assert!(!state.is_poisoned()); /// }); - #[unstable(feature = "once_poison", issue = "33577")] - pub fn poisoned(&self) -> bool { + #[stable(feature = "once_poison", since = "1.51.0")] + pub fn is_poisoned(&self) -> bool { self.poisoned } diff --git a/library/std/src/sync/once/tests.rs b/library/std/src/sync/once/tests.rs index fae2752526..0c35597e11 100644 --- a/library/std/src/sync/once/tests.rs +++ b/library/std/src/sync/once/tests.rs @@ -69,7 +69,7 @@ fn poison_bad() { let mut called = false; O.call_once_force(|p| { called = true; - assert!(p.poisoned()) + assert!(p.is_poisoned()) }); assert!(called); @@ -92,7 +92,7 @@ fn wait_for_force_to_finish() { let (tx2, rx2) = channel(); let t1 = thread::spawn(move || { O.call_once_force(|p| { - assert!(p.poisoned()); + assert!(p.is_poisoned()); tx1.send(()).unwrap(); rx2.recv().unwrap(); }); diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index f988a019cf..885389ca54 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -1,9 +1,10 @@ use crate::cell::UnsafeCell; use crate::collections::VecDeque; use crate::ffi::c_void; +use crate::hint; use crate::ops::{Deref, DerefMut, Drop}; use crate::ptr; -use crate::sync::atomic::{spin_loop_hint, AtomicUsize, Ordering}; +use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::hermit::abi; /// This type provides a lock based on busy waiting to realize mutual exclusion @@ -46,7 +47,7 @@ impl Spinlock { fn obtain_lock(&self) { let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1; while self.dequeue.load(Ordering::SeqCst) != ticket { - spin_loop_hint(); + hint::spin_loop(); } } diff --git a/library/std/src/sys/sgx/waitqueue/spin_mutex.rs b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs index 9140041c58..7f1a671bab 100644 --- a/library/std/src/sys/sgx/waitqueue/spin_mutex.rs +++ b/library/std/src/sys/sgx/waitqueue/spin_mutex.rs @@ -2,8 +2,9 @@ mod tests; use crate::cell::UnsafeCell; +use crate::hint; use crate::ops::{Deref, DerefMut}; -use crate::sync::atomic::{spin_loop_hint, AtomicBool, Ordering}; +use crate::sync::atomic::{AtomicBool, Ordering}; #[derive(Default)] pub struct SpinMutex { @@ -32,7 +33,7 @@ impl SpinMutex { match self.try_lock() { None => { while self.lock.load(Ordering::Relaxed) { - spin_loop_hint() + hint::spin_loop() } } Some(guard) => return guard, diff --git a/library/std/src/sys/unix/ext/process.rs b/library/std/src/sys/unix/ext/process.rs index 3615a8a5ee..724b5dcca6 100644 --- a/library/std/src/sys/unix/ext/process.rs +++ b/library/std/src/sys/unix/ext/process.rs @@ -9,6 +9,14 @@ use crate::process; use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +mod private { + /// This trait being unreachable from outside the crate + /// prevents other implementations of the `ExitStatusExt` trait, + /// which allows potentially adding more trait methods in the future. + #[stable(feature = "none", since = "1.51.0")] + pub trait Sealed {} +} + /// Unix-specific extensions to the [`process::Command`] builder. #[stable(feature = "rust1", since = "1.0.0")] pub trait CommandExt { @@ -31,6 +39,15 @@ pub trait CommandExt { #[cfg(target_os = "vxworks")] id: u16, ) -> &mut process::Command; + /// Sets the supplementary group IDs for the calling process. Translates to + /// a `setgroups` call in the child process. + #[unstable(feature = "setgroups", issue = "38527", reason = "")] + fn groups( + &mut self, + #[cfg(not(target_os = "vxworks"))] groups: &[u32], + #[cfg(target_os = "vxworks")] groups: &[u16], + ) -> &mut process::Command; + /// Schedules a closure to be run just before the `exec` function is /// invoked. /// @@ -141,6 +158,15 @@ impl CommandExt for process::Command { self } + fn groups( + &mut self, + #[cfg(not(target_os = "vxworks"))] groups: &[u32], + #[cfg(target_os = "vxworks")] groups: &[u16], + ) -> &mut process::Command { + self.as_inner_mut().groups(groups); + self + } + unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command where F: FnMut() -> io::Result<()> + Send + Sync + 'static, @@ -163,18 +189,48 @@ impl CommandExt for process::Command { } /// Unix-specific extensions to [`process::ExitStatus`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +/// This is so that future additional methods are not breaking changes. #[stable(feature = "rust1", since = "1.0.0")] -pub trait ExitStatusExt { +pub trait ExitStatusExt: private::Sealed { /// Creates a new `ExitStatus` from the raw underlying `i32` return value of /// a process. #[stable(feature = "exit_status_from", since = "1.12.0")] fn from_raw(raw: i32) -> Self; /// If the process was terminated by a signal, returns that signal. + /// + /// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`. #[stable(feature = "rust1", since = "1.0.0")] fn signal(&self) -> Option; + + /// If the process was terminated by a signal, says whether it dumped core. + #[unstable(feature = "unix_process_wait_more", issue = "80695")] + fn core_dumped(&self) -> bool; + + /// If the process was stopped by a signal, returns that signal. + /// + /// In other words, if `WIFSTOPPED`, this returns `WSTOPSIG`. This is only possible if the status came from + /// a `wait` system call which was passed `WUNTRACED`, was then converted into an `ExitStatus`. + #[unstable(feature = "unix_process_wait_more", issue = "80695")] + fn stopped_signal(&self) -> Option; + + /// Whether the process was continued from a stopped status. + /// + /// Ie, `WIFCONTINUED`. This is only possible if the status came from a `wait` system call + /// which was passed `WCONTINUED`, was then converted into an `ExitStatus`. + #[unstable(feature = "unix_process_wait_more", issue = "80695")] + fn continued(&self) -> bool; + + /// Returns the underlying raw `wait` status. + #[unstable(feature = "unix_process_wait_more", issue = "80695")] + fn into_raw(self) -> i32; } +#[stable(feature = "none", since = "1.51.0")] +impl private::Sealed for process::ExitStatus {} + #[stable(feature = "rust1", since = "1.0.0")] impl ExitStatusExt for process::ExitStatus { fn from_raw(raw: i32) -> Self { @@ -184,6 +240,22 @@ impl ExitStatusExt for process::ExitStatus { fn signal(&self) -> Option { self.as_inner().signal() } + + fn core_dumped(&self) -> bool { + self.as_inner().core_dumped() + } + + fn stopped_signal(&self) -> Option { + self.as_inner().stopped_signal() + } + + fn continued(&self) -> bool { + self.as_inner().continued() + } + + fn into_raw(self) -> i32 { + self.as_inner().into_raw().into() + } } #[stable(feature = "process_extensions", since = "1.2.0")] diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 200dbf06ff..9687576bb6 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -61,6 +61,7 @@ use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; +use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV}; #[cfg(test)] mod tests; @@ -535,7 +536,7 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0)) }; - if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(libc::EBADF))) { + if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(EBADF))) { HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed); } else { HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed); @@ -573,19 +574,20 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> Err(err) => { return match err.raw_os_error() { // when file offset + max_length > u64::MAX - Some(libc::EOVERFLOW) => CopyResult::Fallback(written), - Some( - libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP, - ) => { + Some(EOVERFLOW) => CopyResult::Fallback(written), + Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) => { // Try fallback io::copy if either: // - Kernel version is < 4.5 (ENOSYS¹) // - Files are mounted on different fs (EXDEV) // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP) // - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM) // - copy_file_range cannot be used with pipes or device nodes (EINVAL) + // - the writer fd was opened with O_APPEND (EBADF²) // // ¹ these cases should be detected by the initial probe but we handle them here // anyway in case syscall interception changes during runtime + // ² actually invalid file descriptors would cause this too, but in that case + // the fallback code path is expected to encounter the same error again assert_eq!(written, 0); CopyResult::Fallback(0) } @@ -649,7 +651,7 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> Ok(ret) => written += ret as u64, Err(err) => { return match err.raw_os_error() { - Some(libc::ENOSYS | libc::EPERM) => { + Some(ENOSYS | EPERM) => { // syscall not supported (ENOSYS) // syscall is disallowed, e.g. by seccomp (EPERM) match mode { @@ -659,12 +661,12 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> assert_eq!(written, 0); CopyResult::Fallback(0) } - Some(libc::EINVAL) => { + Some(EINVAL) => { // splice/sendfile do not support this particular file descriptor (EINVAL) assert_eq!(written, 0); CopyResult::Fallback(0) } - Some(os_err) if mode == SpliceMode::Sendfile && os_err == libc::EOVERFLOW => { + Some(os_err) if mode == SpliceMode::Sendfile && os_err == EOVERFLOW => { CopyResult::Fallback(written) } _ => CopyResult::Error(err, written), diff --git a/library/std/src/sys/unix/kernel_copy/tests.rs b/library/std/src/sys/unix/kernel_copy/tests.rs index 77369cdd35..3fe849e23e 100644 --- a/library/std/src/sys/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/unix/kernel_copy/tests.rs @@ -65,6 +65,24 @@ fn copy_specialization() -> Result<()> { result.and(rm1).and(rm2) } +#[test] +fn copies_append_mode_sink() -> Result<()> { + let tmp_path = tmpdir(); + let source_path = tmp_path.join("copies_append_mode.source"); + let sink_path = tmp_path.join("copies_append_mode.sink"); + let mut source = + OpenOptions::new().create(true).truncate(true).write(true).read(true).open(&source_path)?; + write!(source, "not empty")?; + source.seek(SeekFrom::Start(0))?; + let mut sink = OpenOptions::new().create(true).append(true).open(&sink_path)?; + + let copied = crate::io::copy(&mut source, &mut sink)?; + + assert_eq!(copied, 9); + + Ok(()) +} + #[bench] fn bench_file_to_file_copy(b: &mut test::Bencher) { const BYTES: usize = 128 * 1024; diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 7198a2f08d..1df90c18c7 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -208,7 +208,7 @@ impl Socket { Ok(Socket(FileDesc::new(fd))) // While the Android kernel supports the syscall, // it is not included in all versions of Android's libc. - } else if #[cfg(target_os = "android")] { + } else if #[cfg(all(target_os = "android", not(target_arch = "x86")))] { let fd = cvt_r(|| unsafe { libc::syscall(libc::SYS_accept4, self.0.raw(), storage, len, libc::SOCK_CLOEXEC) })?; diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 372e5e6a5b..a96d4aa6a4 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -87,6 +87,7 @@ pub struct Command { gid: Option, saw_nul: bool, closures: Vec io::Result<()> + Send + Sync>>, + groups: Option>, stdin: Option, stdout: Option, stderr: Option, @@ -148,6 +149,7 @@ impl Command { gid: None, saw_nul, closures: Vec::new(), + groups: None, stdin: None, stdout: None, stderr: None, @@ -183,6 +185,9 @@ impl Command { pub fn gid(&mut self, id: gid_t) { self.gid = Some(id); } + pub fn groups(&mut self, groups: &[gid_t]) { + self.groups = Some(Box::from(groups)); + } pub fn saw_nul(&self) -> bool { self.saw_nul @@ -226,6 +231,10 @@ impl Command { pub fn get_gid(&self) -> Option { self.gid } + #[allow(dead_code)] + pub fn get_groups(&self) -> Option<&[gid_t]> { + self.groups.as_deref() + } pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { &mut self.closures diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs index b64636c3f3..0d4703d7f5 100644 --- a/library/std/src/sys/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/unix/process/process_fuchsia.rs @@ -245,6 +245,50 @@ impl ExitStatus { pub fn signal(&self) -> Option { None } + + // FIXME: The actually-Unix implementation in process_unix.rs uses WSTOPSIG, WCOREDUMP et al. + // I infer from the implementation of `success`, `code` and `signal` above that these are not + // available on Fuchsia. + // + // It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many + // other things from std::os::unix) properly. This veneer is always going to be a bodge. So + // while I don't know if these implementations are actually correct, I think they will do for + // now at least. + pub fn core_dumped(&self) -> bool { + false + } + pub fn stopped_signal(&self) -> Option { + None + } + pub fn continued(&self) -> bool { + false + } + + pub fn into_raw(&self) -> c_int { + // We don't know what someone who calls into_raw() will do with this value, but it should + // have the conventional Unix representation. Despite the fact that this is not + // standardised in SuS or POSIX, all Unix systems encode the signal and exit status the + // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every + // Unix.) + // + // The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may + // do their own shifting and masking, or even pass the status to another computer running a + // different Unix variant. + // + // The other view would be to say that the caller on Fuchsia ought to know that `into_raw` + // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is + // not possible here becaause we must return a c_int because that's what Unix (including + // SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't + // necessarily fit. + // + // It seems to me that that the right answer would be to provide std::os::fuchsia with its + // own ExitStatusExt, rather that trying to provide a not very convincing imitation of + // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But + // fixing this up that is beyond the scope of my efforts now. + let exit_status_as_if_unix: u8 = self.0.try_into().expect("Fuchsia process return code bigger than 8 bits, but std::os::unix::ExitStatusExt::into_raw() was called to try to convert the value into a traditional Unix-style wait status, which cannot represent values greater than 255."); + let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8; + wait_status_as_if_unix + } } /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index a590c74435..2746f87468 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -183,20 +183,26 @@ impl Command { #[cfg(not(target_os = "l4re"))] { + if let Some(_g) = self.get_groups() { + //FIXME: Redox kernel does not support setgroups yet + #[cfg(not(target_os = "redox"))] + cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?; + } if let Some(u) = self.get_gid() { cvt(libc::setgid(u as gid_t))?; } if let Some(u) = self.get_uid() { // When dropping privileges from root, the `setgroups` call - // will remove any extraneous groups. If we don't call this, - // then even though our uid has dropped, we may still have - // groups that enable us to do super-user things. This will - // fail if we aren't root, so don't bother checking the - // return value, this is just done as an optimistic - // privilege dropping function. + // will remove any extraneous groups. We only drop groups + // if the current uid is 0 and we weren't given an explicit + // set of groups. If we don't call this, then even though our + // uid has dropped, we may still have groups that enable us to + // do super-user things. //FIXME: Redox kernel does not support setgroups yet #[cfg(not(target_os = "redox"))] - let _ = libc::setgroups(0, ptr::null()); + if libc::getuid() == 0 && self.get_groups().is_none() { + cvt(libc::setgroups(0, ptr::null()))?; + } cvt(libc::setuid(u as uid_t))?; } } @@ -287,6 +293,7 @@ impl Command { || self.get_uid().is_some() || (self.env_saw_path() && !self.program_is_path()) || !self.get_closures().is_empty() + || self.get_groups().is_some() { return Ok(None); } @@ -314,10 +321,20 @@ impl Command { ) -> libc::c_int } let addchdir = match self.get_cwd() { - Some(cwd) => match posix_spawn_file_actions_addchdir_np.get() { - Some(f) => Some((f, cwd)), - None => return Ok(None), - }, + Some(cwd) => { + if cfg!(target_os = "macos") { + // There is a bug in macOS where a relative executable + // path like "../myprogram" will cause `posix_spawn` to + // successfully launch the program, but erroneously return + // ENOENT when used with posix_spawn_file_actions_addchdir_np + // which was introduced in macOS 10.15. + return Ok(None); + } + match posix_spawn_file_actions_addchdir_np.get() { + Some(f) => Some((f, cwd)), + None => return Ok(None), + } + } None => None, }; @@ -479,7 +496,23 @@ impl ExitStatus { } pub fn signal(&self) -> Option { - if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None } + if libc::WIFSIGNALED(self.0) { Some(libc::WTERMSIG(self.0)) } else { None } + } + + pub fn core_dumped(&self) -> bool { + libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0) + } + + pub fn stopped_signal(&self) -> Option { + if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None } + } + + pub fn continued(&self) -> bool { + libc::WIFCONTINUED(self.0) + } + + pub fn into_raw(&self) -> c_int { + self.0 } } diff --git a/library/std/src/sys/unix/process/zircon.rs b/library/std/src/sys/unix/process/zircon.rs index 69ec275c2b..58427bb8b6 100644 --- a/library/std/src/sys/unix/process/zircon.rs +++ b/library/std/src/sys/unix/process/zircon.rs @@ -1,7 +1,6 @@ #![allow(non_camel_case_types, unused)] use crate::convert::TryInto; -use crate::i64; use crate::io; use crate::mem::MaybeUninit; use crate::os::raw::c_char; diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index fac4b05ad0..23a5c81c00 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -237,7 +237,7 @@ mod inner { // `denom` field. // // Encoding this as a single `AtomicU64` allows us to use `Relaxed` - // operations, as we are only interested in in the effects on a single + // operations, as we are only interested in the effects on a single // memory location. static INFO_BITS: AtomicU64 = AtomicU64::new(0); diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs index e93a4972ca..432fe4c33b 100644 --- a/library/std/src/sys/unix/weak.rs +++ b/library/std/src/sys/unix/weak.rs @@ -28,7 +28,7 @@ use crate::sync::atomic::{self, AtomicUsize, Ordering}; macro_rules! weak { (fn $name:ident($($t:ty),*) -> $ret:ty) => ( - static $name: crate::sys::weak::Weak $ret> = + static $name: crate::sys::weak::Weak $ret> = crate::sys::weak::Weak::new(concat!(stringify!($name), '\0')); ) } diff --git a/library/std/src/sys/wasi/ext/fs.rs b/library/std/src/sys/wasi/ext/fs.rs index 4f7cf6018d..a8da003d55 100644 --- a/library/std/src/sys/wasi/ext/fs.rs +++ b/library/std/src/sys/wasi/ext/fs.rs @@ -514,3 +514,11 @@ pub fn symlink, U: AsRef>( .fd() .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?) } + +/// Create a symbolic link. +/// +/// This is a convenience API similar to [`std::os::unix::fs::symlink`] and +/// [`std::os::windows::fs::symlink_file`] and [`symlink_dir`](std::os::windows::fs::symlink_dir). +pub fn symlink_path, U: AsRef>(old_path: P, new_path: U) -> io::Result<()> { + crate::sys::fs::symlink(old_path.as_ref(), new_path.as_ref()) +} diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index 120b9f59f1..a119e66d93 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -627,33 +627,48 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result { /// to any pre-opened file descriptor. fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { let p = CString::new(p.as_os_str().as_bytes())?; - unsafe { - let mut ret = ptr::null(); - let fd = __wasilibc_find_relpath(p.as_ptr(), &mut ret); - if fd == -1 { - let msg = format!( - "failed to find a pre-opened file descriptor \ - through which {:?} could be opened", - p + let mut buf = Vec::::with_capacity(512); + loop { + unsafe { + let mut relative_path = buf.as_ptr().cast(); + let mut abs_prefix = ptr::null(); + let fd = __wasilibc_find_relpath( + p.as_ptr(), + &mut abs_prefix, + &mut relative_path, + buf.capacity(), ); - return Err(io::Error::new(io::ErrorKind::Other, msg)); - } - let path = Path::new(OsStr::from_bytes(CStr::from_ptr(ret).to_bytes())); - - // FIXME: right now `path` is a pointer into `p`, the `CString` above. - // When we return `p` is deallocated and we can't use it, so we need to - // currently separately allocate `path`. If this becomes an issue though - // we should probably turn this into a closure-taking interface or take - // `&CString` and then pass off `&Path` tied to the same lifetime. - let path = path.to_path_buf(); + if fd == -1 { + if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) { + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); + continue; + } + let msg = format!( + "failed to find a pre-opened file descriptor \ + through which {:?} could be opened", + p + ); + return Err(io::Error::new(io::ErrorKind::Other, msg)); + } + let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); - return Ok((ManuallyDrop::new(WasiFd::from_raw(fd as u32)), path)); + return Ok(( + ManuallyDrop::new(WasiFd::from_raw(fd as u32)), + PathBuf::from(OsString::from_vec(relative)), + )); + } } extern "C" { pub fn __wasilibc_find_relpath( path: *const libc::c_char, + abs_prefix: *mut *const libc::c_char, relative_path: *mut *const libc::c_char, + relative_path_len: libc::size_t, ) -> libc::c_int; } } diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index 33c796ae94..185d6109cb 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -13,6 +13,16 @@ use crate::sys::memchr; use crate::sys::{unsupported, Void}; use crate::vec; +// Add a few symbols not in upstream `libc` just yet. +mod libc { + pub use libc::*; + + extern "C" { + pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char; + pub fn chdir(dir: *const c_char) -> c_int; + } +} + #[cfg(not(target_feature = "atomics"))] pub unsafe fn env_lock() -> impl Any { // No need for a lock if we're single-threaded, but this function will need @@ -41,11 +51,40 @@ pub fn error_string(errno: i32) -> String { } pub fn getcwd() -> io::Result { - unsupported() + let mut buf = Vec::with_capacity(512); + loop { + unsafe { + let ptr = buf.as_mut_ptr() as *mut libc::c_char; + if !libc::getcwd(ptr, buf.capacity()).is_null() { + let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len(); + buf.set_len(len); + buf.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(buf))); + } else { + let error = io::Error::last_os_error(); + if error.raw_os_error() != Some(libc::ERANGE) { + return Err(error); + } + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); + } + } } -pub fn chdir(_: &path::Path) -> io::Result<()> { - unsupported() +pub fn chdir(p: &path::Path) -> io::Result<()> { + let p: &OsStr = p.as_ref(); + let p = CString::new(p.as_bytes())?; + unsafe { + match libc::chdir(p.as_ptr()) == (0 as libc::c_int) { + true => Ok(()), + false => Err(io::Error::last_os_error()), + } + } } pub struct SplitPaths<'a>(&'a Void); diff --git a/library/std/src/sys/wasm/thread.rs b/library/std/src/sys/wasm/thread.rs index 95a9230aa7..5eafb77da1 100644 --- a/library/std/src/sys/wasm/thread.rs +++ b/library/std/src/sys/wasm/thread.rs @@ -86,7 +86,7 @@ pub fn my_id() -> u32 { if MY_ID == 0 { let mut cur = NEXT_ID.load(SeqCst); MY_ID = loop { - let next = cur.checked_add(1).unwrap_or_else(|| crate::arch::wasm32::unreachable()); + let next = cur.checked_add(1).unwrap_or_else(|| crate::process::abort()); match NEXT_ID.compare_exchange(cur, next, SeqCst, SeqCst) { Ok(_) => break next, Err(i) => cur = i, diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 2b1bc92dc8..dec8862081 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -975,6 +975,7 @@ extern "system" { pub fn freeaddrinfo(res: *mut ADDRINFOA); pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void; + pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE; pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE; pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME); @@ -1020,6 +1021,60 @@ extern "system" { pub fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; pub fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID; pub fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; + + // >= Vista / Server 2008 + // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinka + pub fn CreateSymbolicLinkW( + lpSymlinkFileName: LPCWSTR, + lpTargetFileName: LPCWSTR, + dwFlags: DWORD, + ) -> BOOLEAN; + + // >= Vista / Server 2008 + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew + pub fn GetFinalPathNameByHandleW( + hFile: HANDLE, + lpszFilePath: LPCWSTR, + cchFilePath: DWORD, + dwFlags: DWORD, + ) -> DWORD; + + // >= Vista / Server 2003 + // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadstackguarantee + #[cfg(not(target_vendor = "uwp"))] + pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL; + + // >= Vista / Server 2008 + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle + pub fn SetFileInformationByHandle( + hFile: HANDLE, + FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, + lpFileInformation: LPVOID, + dwBufferSize: DWORD, + ) -> BOOL; + + // >= Vista / Server 2008 + // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleepconditionvariablesrw + pub fn SleepConditionVariableSRW( + ConditionVariable: PCONDITION_VARIABLE, + SRWLock: PSRWLOCK, + dwMilliseconds: DWORD, + Flags: ULONG, + ) -> BOOL; + + // >= Vista / Server 2008 + // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-wakeconditionvariable + pub fn WakeConditionVariable(ConditionVariable: PCONDITION_VARIABLE); + pub fn WakeAllConditionVariable(ConditionVariable: PCONDITION_VARIABLE); + + // >= Vista / Server 2008 + // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-acquiresrwlockexclusive + pub fn AcquireSRWLockExclusive(SRWLock: PSRWLOCK); + pub fn AcquireSRWLockShared(SRWLock: PSRWLOCK); + pub fn ReleaseSRWLockExclusive(SRWLock: PSRWLOCK); + pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK); + pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN; + pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN; } // Functions that aren't available on every version of Windows that we support, @@ -1027,70 +1082,26 @@ extern "system" { compat_fn! { "kernel32": - pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, - _lpTargetFileName: LPCWSTR, - _dwFlags: DWORD) -> BOOLEAN { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 - } - pub fn GetFinalPathNameByHandleW(_hFile: HANDLE, - _lpszFilePath: LPCWSTR, - _cchFilePath: DWORD, - _dwFlags: DWORD) -> DWORD { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 - } - #[cfg(not(target_vendor = "uwp"))] - pub fn SetThreadStackGuarantee(_size: *mut c_ulong) -> BOOL { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 - } + // >= Win10 1607 + // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription pub fn SetThreadDescription(hThread: HANDLE, lpThreadDescription: LPCWSTR) -> HRESULT { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); E_NOTIMPL } - pub fn SetFileInformationByHandle(_hFile: HANDLE, - _FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, - _lpFileInformation: LPVOID, - _dwBufferSize: DWORD) -> BOOL { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 - } + + // >= Win8 / Server 2012 + // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime pub fn GetSystemTimePreciseAsFileTime(lpSystemTimeAsFileTime: LPFILETIME) -> () { GetSystemTimeAsFileTime(lpSystemTimeAsFileTime) } - pub fn SleepConditionVariableSRW(ConditionVariable: PCONDITION_VARIABLE, - SRWLock: PSRWLOCK, - dwMilliseconds: DWORD, - Flags: ULONG) -> BOOL { - panic!("condition variables not available") - } - pub fn WakeConditionVariable(ConditionVariable: PCONDITION_VARIABLE) - -> () { - panic!("condition variables not available") - } - pub fn WakeAllConditionVariable(ConditionVariable: PCONDITION_VARIABLE) - -> () { - panic!("condition variables not available") - } - pub fn AcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> () { - panic!("rwlocks not available") - } - pub fn AcquireSRWLockShared(SRWLock: PSRWLOCK) -> () { - panic!("rwlocks not available") - } - pub fn ReleaseSRWLockExclusive(SRWLock: PSRWLOCK) -> () { - panic!("rwlocks not available") - } - pub fn ReleaseSRWLockShared(SRWLock: PSRWLOCK) -> () { - panic!("rwlocks not available") - } - pub fn TryAcquireSRWLockExclusive(SRWLock: PSRWLOCK) -> BOOLEAN { - panic!("rwlocks not available") - } - pub fn TryAcquireSRWLockShared(SRWLock: PSRWLOCK) -> BOOLEAN { - panic!("rwlocks not available") - } } + compat_fn! { "api-ms-win-core-synch-l1-2-0": + + // >= Windows 8 / Server 2012 + // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitonaddress pub fn WaitOnAddress( Address: LPVOID, CompareAddress: LPVOID, diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index e9588e2975..cbd3366b18 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -1,93 +1,114 @@ -//! A "compatibility layer" for spanning XP and Windows 7 +//! A "compatibility layer" for supporting older versions of Windows //! -//! The standard library currently binds many functions that are not available -//! on Windows XP, but we would also like to support building executables that -//! run on XP. To do this we specify all non-XP APIs as having a fallback -//! implementation to do something reasonable. +//! The standard library uses some Windows API functions that are not present +//! on older versions of Windows. (Note that the oldest version of Windows +//! that Rust supports is Windows 7 (client) and Windows Server 2008 (server).) +//! This module implements a form of delayed DLL import binding, using +//! `GetModuleHandle` and `GetProcAddress` to look up DLL entry points at +//! runtime. //! -//! This dynamic runtime detection of whether a function is available is -//! implemented with `GetModuleHandle` and `GetProcAddress` paired with a -//! static-per-function which caches the result of the first check. In this -//! manner we pay a semi-large one-time cost up front for detecting whether a -//! function is available but afterwards it's just a load and a jump. - -use crate::ffi::CString; -use crate::sys::c; - -pub fn lookup(module: &str, symbol: &str) -> Option { - let mut module: Vec = module.encode_utf16().collect(); - module.push(0); - let symbol = CString::new(symbol).unwrap(); - unsafe { - let handle = c::GetModuleHandleW(module.as_ptr()); - match c::GetProcAddress(handle, symbol.as_ptr()) as usize { - 0 => None, - n => Some(n), - } - } -} +//! This implementation uses a static initializer to look up the DLL entry +//! points. The CRT (C runtime) executes static initializers before `main` +//! is called (for binaries) and before `DllMain` is called (for DLLs). +//! This is the ideal time to look up DLL imports, because we are guaranteed +//! that no other threads will attempt to call these entry points. Thus, +//! we can look up the imports and store them in `static mut` fields +//! without any synchronization. +//! +//! This has an additional advantage: Because the DLL import lookup happens +//! at module initialization, the cost of these lookups is deterministic, +//! and is removed from the code paths that actually call the DLL imports. +//! That is, there is no unpredictable "cache miss" that occurs when calling +//! a DLL import. For applications that benefit from predictable delays, +//! this is a benefit. This also eliminates the comparison-and-branch +//! from the hot path. +//! +//! Currently, the standard library uses only a small number of dynamic +//! DLL imports. If this number grows substantially, then the cost of +//! performing all of the lookups at initialization time might become +//! substantial. +//! +//! The mechanism of registering a static initializer with the CRT is +//! documented in +//! [CRT Initialization](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160). +//! It works by contributing a global symbol to the `.CRT$XCU` section. +//! The linker builds a table of all static initializer functions. +//! The CRT startup code then iterates that table, calling each +//! initializer function. +//! +//! # **WARNING!!* +//! The environment that a static initializer function runs in is highly +//! constrained. There are **many** restrictions on what static initializers +//! can safely do. Static initializer functions **MUST NOT** do any of the +//! following (this list is not comprehensive): +//! * touch any other static field that is used by a different static +//! initializer, because the order that static initializers run in +//! is not defined. +//! * call `LoadLibrary` or any other function that acquires the DLL +//! loader lock. +//! * call any Rust function or CRT function that touches any static +//! (global) state. macro_rules! compat_fn { ($module:literal: $( $(#[$meta:meta])* - pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $body:block + pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block )*) => ($( $(#[$meta])* pub mod $symbol { #[allow(unused_imports)] use super::*; - use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::mem; type F = unsafe extern "system" fn($($argtype),*) -> $rettype; - static PTR: AtomicUsize = AtomicUsize::new(0); - - #[allow(unused_variables)] - unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype $body - - /// This address is stored in `PTR` to incidate an unavailable API. - /// - /// This way, call() will end up calling fallback() if it is unavailable. - /// - /// This is a `static` to avoid rustc duplicating `fn fallback()` - /// into both load() and is_available(), which would break - /// is_available()'s comparison. By using the same static variable - /// in both places, they'll refer to the same (copy of the) - /// function. + /// Points to the DLL import, or the fallback function. /// - /// LLVM merging the address of fallback with other functions - /// (because of unnamed_addr) is fine, since it's only compared to - /// an address from GetProcAddress from an external dll. - static FALLBACK: F = fallback; + /// This static can be an ordinary, unsynchronized, mutable static because + /// we guarantee that all of the writes finish during CRT initialization, + /// and all of the reads occur after CRT initialization. + static mut PTR: Option = None; - #[cold] - fn load() -> usize { - // There is no locking here. It's okay if this is executed by multiple threads in - // parallel. `lookup` will result in the same value, and it's okay if they overwrite - // eachothers result as long as they do so atomically. We don't need any guarantees - // about memory ordering, as this involves just a single atomic variable which is - // not used to protect or order anything else. - let addr = crate::sys::compat::lookup($module, stringify!($symbol)) - .unwrap_or(FALLBACK as usize); - PTR.store(addr, Ordering::Relaxed); - addr - } + /// This symbol is what allows the CRT to find the `init` function and call it. + /// It is marked `#[used]` because otherwise Rust would assume that it was not + /// used, and would remove it. + #[used] + #[link_section = ".CRT$XCU"] + static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init; - fn addr() -> usize { - match PTR.load(Ordering::Relaxed) { - 0 => load(), - addr => addr, + unsafe extern "C" fn init() { + // There is no locking here. This code is executed before main() is entered, and + // is guaranteed to be single-threaded. + // + // DO NOT do anything interesting or complicated in this function! DO NOT call + // any Rust functions or CRT functions, if those functions touch any global state, + // because this function runs during global initialization. For example, DO NOT + // do any dynamic allocation, don't call LoadLibrary, etc. + let module_name: *const u8 = concat!($module, "\0").as_ptr(); + let symbol_name: *const u8 = concat!(stringify!($symbol), "\0").as_ptr(); + let module_handle = $crate::sys::c::GetModuleHandleA(module_name as *const i8); + if !module_handle.is_null() { + match $crate::sys::c::GetProcAddress(module_handle, symbol_name as *const i8) as usize { + 0 => {} + n => { + PTR = Some(mem::transmute::(n)); + } + } } } #[allow(dead_code)] - pub fn is_available() -> bool { - addr() != FALLBACK as usize + pub fn option() -> Option { + unsafe { PTR } } + #[allow(dead_code)] pub unsafe fn call($($argname: $argtype),*) -> $rettype { - mem::transmute::(addr())($($argname),*) + if let Some(ptr) = PTR { + ptr($($argname),*) + } else { + $fallback_body + } } } diff --git a/library/std/src/sys/windows/ext/process.rs b/library/std/src/sys/windows/ext/process.rs index 61e4c6a1d1..7a92381d66 100644 --- a/library/std/src/sys/windows/ext/process.rs +++ b/library/std/src/sys/windows/ext/process.rs @@ -7,6 +7,14 @@ use crate::process; use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +mod private { + /// This trait being unreachable from outside the crate + /// prevents other implementations of the `ExitStatusExt` trait, + /// which allows potentially adding more trait methods in the future. + #[stable(feature = "none", since = "1.51.0")] + pub trait Sealed {} +} + #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawHandle for process::Stdio { unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio { @@ -73,8 +81,11 @@ impl IntoRawHandle for process::ChildStderr { } /// Windows-specific extensions to [`process::ExitStatus`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +/// This is so that future additional methods are not breaking changes. #[stable(feature = "exit_status_from", since = "1.12.0")] -pub trait ExitStatusExt { +pub trait ExitStatusExt: private::Sealed { /// Creates a new `ExitStatus` from the raw underlying `u32` return value of /// a process. #[stable(feature = "exit_status_from", since = "1.12.0")] @@ -88,6 +99,9 @@ impl ExitStatusExt for process::ExitStatus { } } +#[stable(feature = "none", since = "1.51.0")] +impl private::Sealed for process::ExitStatus {} + /// Windows-specific extensions to the [`process::Command`] builder. #[stable(feature = "windows_process_extensions", since = "1.16.0")] pub trait CommandExt { diff --git a/library/std/src/sys/windows/mutex.rs b/library/std/src/sys/windows/mutex.rs index d4cc56d4cb..72a0993d94 100644 --- a/library/std/src/sys/windows/mutex.rs +++ b/library/std/src/sys/windows/mutex.rs @@ -13,20 +13,13 @@ //! //! 3. While CriticalSection is fair and SRWLock is not, the current Rust policy //! is that there are no guarantees of fairness. -//! -//! The downside of this approach, however, is that SRWLock is not available on -//! Windows XP, so we continue to have a fallback implementation where -//! CriticalSection is used and we keep track of who's holding the mutex to -//! detect recursive locks. - -use crate::cell::{Cell, UnsafeCell}; -use crate::mem::{self, MaybeUninit}; -use crate::sync::atomic::{AtomicUsize, Ordering}; + +use crate::cell::UnsafeCell; +use crate::mem::MaybeUninit; use crate::sys::c; pub struct Mutex { - // This is either directly an SRWLOCK (if supported), or a Box otherwise. - lock: AtomicUsize, + srwlock: UnsafeCell, } // Windows SRW Locks are movable (while not borrowed). @@ -37,106 +30,39 @@ pub type MovableMutex = Mutex; unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} -struct Inner { - remutex: ReentrantMutex, - held: Cell, -} - -#[derive(Clone, Copy)] -enum Kind { - SRWLock, - CriticalSection, -} - #[inline] pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK { - debug_assert!(mem::size_of::() <= mem::size_of_val(&m.lock)); - &m.lock as *const _ as *mut _ + m.srwlock.get() } impl Mutex { pub const fn new() -> Mutex { - Mutex { - // This works because SRWLOCK_INIT is 0 (wrapped in a struct), so we are also properly - // initializing an SRWLOCK here. - lock: AtomicUsize::new(0), - } + Mutex { srwlock: UnsafeCell::new(c::SRWLOCK_INIT) } } #[inline] pub unsafe fn init(&mut self) {} + + #[inline] pub unsafe fn lock(&self) { - match kind() { - Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)), - Kind::CriticalSection => { - let inner = &*self.inner(); - inner.remutex.lock(); - if inner.held.replace(true) { - // It was already locked, so we got a recursive lock which we do not want. - inner.remutex.unlock(); - panic!("cannot recursively lock a mutex"); - } - } - } + c::AcquireSRWLockExclusive(raw(self)); } + + #[inline] pub unsafe fn try_lock(&self) -> bool { - match kind() { - Kind::SRWLock => c::TryAcquireSRWLockExclusive(raw(self)) != 0, - Kind::CriticalSection => { - let inner = &*self.inner(); - if !inner.remutex.try_lock() { - false - } else if inner.held.replace(true) { - // It was already locked, so we got a recursive lock which we do not want. - inner.remutex.unlock(); - false - } else { - true - } - } - } + c::TryAcquireSRWLockExclusive(raw(self)) != 0 } + + #[inline] pub unsafe fn unlock(&self) { - match kind() { - Kind::SRWLock => c::ReleaseSRWLockExclusive(raw(self)), - Kind::CriticalSection => { - let inner = &*(self.lock.load(Ordering::SeqCst) as *const Inner); - inner.held.set(false); - inner.remutex.unlock(); - } - } - } - pub unsafe fn destroy(&self) { - match kind() { - Kind::SRWLock => {} - Kind::CriticalSection => match self.lock.load(Ordering::SeqCst) { - 0 => {} - n => Box::from_raw(n as *mut Inner).remutex.destroy(), - }, - } + c::ReleaseSRWLockExclusive(raw(self)); } - unsafe fn inner(&self) -> *const Inner { - match self.lock.load(Ordering::SeqCst) { - 0 => {} - n => return n as *const _, - } - let inner = box Inner { remutex: ReentrantMutex::uninitialized(), held: Cell::new(false) }; - inner.remutex.init(); - let inner = Box::into_raw(inner); - match self.lock.compare_exchange(0, inner as usize, Ordering::SeqCst, Ordering::SeqCst) { - Ok(_) => inner, - Err(n) => { - Box::from_raw(inner).remutex.destroy(); - n as *const _ - } - } + #[inline] + pub unsafe fn destroy(&self) { + // SRWLock does not need to be destroyed. } } -fn kind() -> Kind { - if c::AcquireSRWLockExclusive::is_available() { Kind::SRWLock } else { Kind::CriticalSection } -} - pub struct ReentrantMutex { inner: MaybeUninit>, } diff --git a/library/std/src/sys/windows/thread_parker.rs b/library/std/src/sys/windows/thread_parker.rs index 9e4c9aa0a5..4f59d4dd45 100644 --- a/library/std/src/sys/windows/thread_parker.rs +++ b/library/std/src/sys/windows/thread_parker.rs @@ -108,10 +108,10 @@ impl Parker { return; } - if c::WaitOnAddress::is_available() { + if let Some(wait_on_address) = c::WaitOnAddress::option() { loop { // Wait for something to happen, assuming it's still set to PARKED. - c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE); + wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE); // Change NOTIFIED=>EMPTY but leave PARKED alone. if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() { // Actually woken up by unpark(). @@ -140,9 +140,9 @@ impl Parker { return; } - if c::WaitOnAddress::is_available() { + if let Some(wait_on_address) = c::WaitOnAddress::option() { // Wait for something to happen, assuming it's still set to PARKED. - c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout)); + wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout)); // Set the state back to EMPTY (from either PARKED or NOTIFIED). // Note that we don't just write EMPTY, but use swap() to also // include an acquire-ordered read to synchronize with unpark()'s @@ -192,9 +192,9 @@ impl Parker { // purpose, to make sure every unpark() has a release-acquire ordering // with park(). if self.state.swap(NOTIFIED, Release) == PARKED { - if c::WakeByAddressSingle::is_available() { + if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() { unsafe { - c::WakeByAddressSingle(self.ptr()); + wake_by_address_single(self.ptr()); } } else { // If we run NtReleaseKeyedEvent before the waiting thread runs diff --git a/library/std/src/sys_common/fs.rs b/library/std/src/sys_common/fs.rs index e30e8018a3..6bdb26cd07 100644 --- a/library/std/src/sys_common/fs.rs +++ b/library/std/src/sys_common/fs.rs @@ -5,19 +5,21 @@ use crate::io::{self, Error, ErrorKind}; use crate::path::Path; pub fn copy(from: &Path, to: &Path) -> io::Result { - if !from.is_file() { + let mut reader = fs::File::open(from)?; + let metadata = reader.metadata()?; + + if !metadata.is_file() { return Err(Error::new( ErrorKind::InvalidInput, "the source path is not an existing regular file", )); } - let mut reader = fs::File::open(from)?; let mut writer = fs::File::create(to)?; - let perm = reader.metadata()?.permissions(); + let perm = metadata.permissions(); let ret = io::copy(&mut reader, &mut writer)?; - fs::set_permissions(to, perm)?; + writer.set_permissions(perm)?; Ok(ret) } diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 5d65f960fc..0d004a516f 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1186,32 +1186,37 @@ impl fmt::Debug for Thread { /// the [`Error`](crate::error::Error) trait. /// /// Thus, a sensible way to handle a thread panic is to either: -/// 1. `unwrap` the `Result`, propagating the panic +/// +/// 1. propagate the panic with [`std::panic::resume_unwind`] /// 2. or in case the thread is intended to be a subsystem boundary /// that is supposed to isolate system-level failures, -/// match on the `Err` variant and handle the panic in an appropriate way. +/// match on the `Err` variant and handle the panic in an appropriate way /// /// A thread that completes without panicking is considered to exit successfully. /// /// # Examples /// +/// Matching on the result of a joined thread: +/// /// ```no_run -/// use std::thread; -/// use std::fs; +/// use std::{fs, thread, panic}; /// /// fn copy_in_thread() -> thread::Result<()> { -/// thread::spawn(move || { fs::copy("foo.txt", "bar.txt").unwrap(); }).join() +/// thread::spawn(|| { +/// fs::copy("foo.txt", "bar.txt").unwrap(); +/// }).join() /// } /// /// fn main() { /// match copy_in_thread() { -/// Ok(_) => println!("this is fine"), -/// Err(_) => println!("thread panicked"), +/// Ok(_) => println!("copy succeeded"), +/// Err(e) => panic::resume_unwind(e), /// } /// } /// ``` /// /// [`Result`]: crate::result::Result +/// [`std::panic::resume_unwind`]: crate::panic::resume_unwind #[stable(feature = "rust1", since = "1.0.0")] pub type Result = crate::result::Result>; diff --git a/library/term/src/terminfo/parm/tests.rs b/library/term/src/terminfo/parm/tests.rs index b975bd2d19..1cc0967c8f 100644 --- a/library/term/src/terminfo/parm/tests.rs +++ b/library/term/src/terminfo/parm/tests.rs @@ -77,15 +77,15 @@ fn test_comparison_ops() { for &(op, bs) in v.iter() { let s = format!("%{{1}}%{{2}}%{}%d", op); let res = expand(s.as_bytes(), &[], &mut Variables::new()); - assert!(res.is_ok(), res.unwrap_err()); + assert!(res.is_ok(), "{}", res.unwrap_err()); assert_eq!(res.unwrap(), vec![b'0' + bs[0]]); let s = format!("%{{1}}%{{1}}%{}%d", op); let res = expand(s.as_bytes(), &[], &mut Variables::new()); - assert!(res.is_ok(), res.unwrap_err()); + assert!(res.is_ok(), "{}", res.unwrap_err()); assert_eq!(res.unwrap(), vec![b'0' + bs[1]]); let s = format!("%{{2}}%{{1}}%{}%d", op); let res = expand(s.as_bytes(), &[], &mut Variables::new()); - assert!(res.is_ok(), res.unwrap_err()); + assert!(res.is_ok(), "{}", res.unwrap_err()); assert_eq!(res.unwrap(), vec![b'0' + bs[2]]); } } @@ -95,13 +95,13 @@ fn test_conditionals() { let mut vars = Variables::new(); let s = b"\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"; let res = expand(s, &[Number(1)], &mut vars); - assert!(res.is_ok(), res.unwrap_err()); + assert!(res.is_ok(), "{}", res.unwrap_err()); assert_eq!(res.unwrap(), "\\E[31m".bytes().collect::>()); let res = expand(s, &[Number(8)], &mut vars); - assert!(res.is_ok(), res.unwrap_err()); + assert!(res.is_ok(), "{}", res.unwrap_err()); assert_eq!(res.unwrap(), "\\E[90m".bytes().collect::>()); let res = expand(s, &[Number(42)], &mut vars); - assert!(res.is_ok(), res.unwrap_err()); + assert!(res.is_ok(), "{}", res.unwrap_err()); assert_eq!(res.unwrap(), "\\E[38;5;42m".bytes().collect::>()); } diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index d5804cc3dd..226557430d 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -26,7 +26,7 @@ default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] backtrace = ["std/backtrace"] compiler-builtins-c = ["std/compiler-builtins-c"] compiler-builtins-mem = ["std/compiler-builtins-mem"] -compiler-builtins-asm = ["std/compiler-builtins-asm"] +compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"] compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"] llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 97a659f22d..02c529252e 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -230,9 +230,9 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { // Unstable flags let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process"); let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic"); - let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored"); let time_options = get_time_options(&matches, allow_unstable)?; + let include_ignored = matches.opt_present("include-ignored"); let quiet = matches.opt_present("quiet"); let exact = matches.opt_present("exact"); let list = matches.opt_present("list"); diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 656d9669e8..2d37fdd135 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -25,6 +25,7 @@ #![feature(nll)] #![feature(available_concurrency)] #![feature(internal_output_capture)] +#![feature(option_unwrap_none)] #![feature(panic_unwind)] #![feature(staged_api)] #![feature(termination_trait_lib)] @@ -61,6 +62,7 @@ pub mod test { } use std::{ + collections::VecDeque, env, io, io::prelude::Write, panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo}, @@ -208,9 +210,19 @@ where use std::collections::{self, HashMap}; use std::hash::BuildHasherDefault; use std::sync::mpsc::RecvTimeoutError; + + struct RunningTest { + join_handle: Option>, + } + // Use a deterministic hasher type TestMap = - HashMap>; + HashMap>; + + struct TimeoutEntry { + desc: TestDesc, + timeout: Instant, + } let tests_len = tests.len(); @@ -255,23 +267,30 @@ where }; let mut running_tests: TestMap = HashMap::default(); + let mut timeout_queue: VecDeque = VecDeque::new(); - fn get_timed_out_tests(running_tests: &mut TestMap) -> Vec { + fn get_timed_out_tests( + running_tests: &TestMap, + timeout_queue: &mut VecDeque, + ) -> Vec { let now = Instant::now(); - let timed_out = running_tests - .iter() - .filter_map(|(desc, timeout)| if &now >= timeout { Some(desc.clone()) } else { None }) - .collect(); - for test in &timed_out { - running_tests.remove(test); + let mut timed_out = Vec::new(); + while let Some(timeout_entry) = timeout_queue.front() { + if now < timeout_entry.timeout { + break; + } + let timeout_entry = timeout_queue.pop_front().unwrap(); + if running_tests.contains_key(&timeout_entry.desc) { + timed_out.push(timeout_entry.desc); + } } timed_out } - fn calc_timeout(running_tests: &TestMap) -> Option { - running_tests.values().min().map(|next_timeout| { + fn calc_timeout(timeout_queue: &VecDeque) -> Option { + timeout_queue.front().map(|&TimeoutEntry { timeout: next_timeout, .. }| { let now = Instant::now(); - if *next_timeout >= now { *next_timeout - now } else { Duration::new(0, 0) } + if next_timeout >= now { next_timeout - now } else { Duration::new(0, 0) } }) } @@ -280,7 +299,8 @@ where let test = remaining.pop().unwrap(); let event = TestEvent::TeWait(test.desc.clone()); notify_about_test_event(event)?; - run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No); + run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No) + .unwrap_none(); let completed_test = rx.recv().unwrap(); let event = TestEvent::TeResult(completed_test); @@ -291,19 +311,28 @@ where while pending < concurrency && !remaining.is_empty() { let test = remaining.pop().unwrap(); let timeout = time::get_default_test_timeout(); - running_tests.insert(test.desc.clone(), timeout); + let desc = test.desc.clone(); - let event = TestEvent::TeWait(test.desc.clone()); + let event = TestEvent::TeWait(desc.clone()); notify_about_test_event(event)?; //here no pad - run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::Yes); + let join_handle = run_test( + opts, + !opts.run_tests, + test, + run_strategy, + tx.clone(), + Concurrent::Yes, + ); + running_tests.insert(desc.clone(), RunningTest { join_handle }); + timeout_queue.push_back(TimeoutEntry { desc, timeout }); pending += 1; } let mut res; loop { - if let Some(timeout) = calc_timeout(&running_tests) { + if let Some(timeout) = calc_timeout(&timeout_queue) { res = rx.recv_timeout(timeout); - for test in get_timed_out_tests(&mut running_tests) { + for test in get_timed_out_tests(&running_tests, &mut timeout_queue) { let event = TestEvent::TeTimeout(test); notify_about_test_event(event)?; } @@ -323,8 +352,17 @@ where } } - let completed_test = res.unwrap(); - running_tests.remove(&completed_test.desc); + let mut completed_test = res.unwrap(); + if let Some(running_test) = running_tests.remove(&completed_test.desc) { + if let Some(join_handle) = running_test.join_handle { + if let Err(_) = join_handle.join() { + if let TrOk = completed_test.result { + completed_test.result = + TrFailedMsg("panicked after reporting success".to_string()); + } + } + } + } let event = TestEvent::TeResult(completed_test); notify_about_test_event(event)?; @@ -415,7 +453,7 @@ pub fn run_test( strategy: RunStrategy, monitor_ch: Sender, concurrency: Concurrent, -) { +) -> Option> { let TestDescAndFn { desc, testfn } = test; // Emscripten can catch panics but other wasm targets cannot @@ -426,7 +464,7 @@ pub fn run_test( if force_ignore || desc.ignore || ignore_because_no_process_support { let message = CompletedTest::new(desc, TrIgnored, None, Vec::new()); monitor_ch.send(message).unwrap(); - return; + return None; } struct TestRunOpts { @@ -441,7 +479,7 @@ pub fn run_test( monitor_ch: Sender, testfn: Box, opts: TestRunOpts, - ) { + ) -> Option> { let concurrency = opts.concurrency; let name = desc.name.clone(); @@ -469,9 +507,10 @@ pub fn run_test( let supports_threads = !cfg!(target_os = "emscripten") && !cfg!(target_arch = "wasm32"); if concurrency == Concurrent::Yes && supports_threads { let cfg = thread::Builder::new().name(name.as_slice().to_owned()); - cfg.spawn(runtest).unwrap(); + Some(cfg.spawn(runtest).unwrap()) } else { runtest(); + None } } @@ -484,10 +523,12 @@ pub fn run_test( crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| { bencher.run(harness) }); + None } StaticBenchFn(benchfn) => { // Benchmarks aren't expected to panic, so we run them all in-process. crate::bench::benchmark(desc, monitor_ch, opts.nocapture, benchfn); + None } DynTestFn(f) => { match strategy { @@ -499,7 +540,7 @@ pub fn run_test( monitor_ch, Box::new(move || __rust_begin_short_backtrace(f)), test_run_opts, - ); + ) } StaticTestFn(f) => run_test_inner( desc, diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 465f3f8f99..598fb670bb 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -63,7 +63,7 @@ pub fn calc_result<'a>( )) } } - (&ShouldPanic::Yes, Ok(())) => { + (&ShouldPanic::Yes, Ok(())) | (&ShouldPanic::YesWithMessage(_), Ok(())) => { TestResult::TrFailedMsg("test did not panic as expected".to_string()) } _ if desc.allow_fail => TestResult::TrAllowedFail, diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index 74313cc443..f0586d510d 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -199,7 +199,7 @@ fn test_should_panic_bad_message() { fn test_should_panic_non_string_message_type() { use crate::tests::TrFailedMsg; fn f() { - panic!(1i32); + std::panic::panic_any(1i32); } let expected = "foobar"; let failed_msg = format!( @@ -228,21 +228,30 @@ fn test_should_panic_non_string_message_type() { #[test] #[cfg(not(target_os = "emscripten"))] fn test_should_panic_but_succeeds() { - fn f() {} - let desc = TestDescAndFn { - desc: TestDesc { - name: StaticTestName("whatever"), - ignore: false, - should_panic: ShouldPanic::Yes, - allow_fail: false, - test_type: TestType::Unknown, - }, - testfn: DynTestFn(Box::new(f)), - }; - let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); - let result = rx.recv().unwrap().result; - assert_eq!(result, TrFailedMsg("test did not panic as expected".to_string())); + let should_panic_variants = [ShouldPanic::Yes, ShouldPanic::YesWithMessage("error message")]; + + for &should_panic in should_panic_variants.iter() { + fn f() {} + let desc = TestDescAndFn { + desc: TestDesc { + name: StaticTestName("whatever"), + ignore: false, + should_panic, + allow_fail: false, + test_type: TestType::Unknown, + }, + testfn: DynTestFn(Box::new(f)), + }; + let (tx, rx) = channel(); + run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + let result = rx.recv().unwrap().result; + assert_eq!( + result, + TrFailedMsg("test did not panic as expected".to_string()), + "should_panic == {:?}", + should_panic + ); + } } fn report_time_test_template(report_time: bool) -> Option { @@ -382,12 +391,7 @@ fn parse_show_output_flag() { #[test] fn parse_include_ignored_flag() { - let args = vec![ - "progname".to_string(), - "filter".to_string(), - "-Zunstable-options".to_string(), - "--include-ignored".to_string(), - ]; + let args = vec!["progname".to_string(), "filter".to_string(), "--include-ignored".to_string()]; let opts = parse_opts(&args).unwrap().unwrap(); assert_eq!(opts.run_ignored, RunIgnored::Yes); } diff --git a/library/unwind/build.rs b/library/unwind/build.rs index fae760c4a4..694e6b98c8 100644 --- a/library/unwind/build.rs +++ b/library/unwind/build.rs @@ -4,6 +4,10 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); let target = env::var("TARGET").expect("TARGET was not set"); + if cfg!(feature = "system-llvm-libunwind") { + return; + } + if cfg!(feature = "llvm-libunwind") && ((target.contains("linux") && !target.contains("musl")) || target.contains("fuchsia")) { diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index ff1d82fc99..faf554d285 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -36,9 +36,12 @@ pub const unwinder_private_data_size: usize = 20; #[cfg(all(target_arch = "arm", target_os = "ios"))] pub const unwinder_private_data_size: usize = 5; -#[cfg(target_arch = "aarch64")] +#[cfg(all(target_arch = "aarch64", target_pointer_width = "64"))] pub const unwinder_private_data_size: usize = 2; +#[cfg(all(target_arch = "aarch64", target_pointer_width = "32"))] +pub const unwinder_private_data_size: usize = 5; + #[cfg(target_arch = "mips")] pub const unwinder_private_data_size: usize = 2; diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index a103c9fb0b..f899f21080 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -4,7 +4,12 @@ All notable changes to bootstrap will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## [Non-breaking changes since the last major version] + +## [Changes since the last major version] + +- `llvm-libunwind` now accepts `in-tree` (formerly true), `system` or `no` (formerly false) [#77703](https://github.com/rust-lang/rust/pull/77703) + +### Non-breaking changes - `x.py check` needs opt-in to check tests (--all-targets) [#77473](https://github.com/rust-lang/rust/pull/77473) - The default bootstrap profiles are now located at `bootstrap/defaults/config.$PROFILE.toml` (previously they were located at `bootstrap/defaults/config.toml.$PROFILE`) [#77558](https://github.com/rust-lang/rust/pull/77558) diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 84ed9446ae..a2e596bf4e 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -201,7 +201,6 @@ build/ # Output for all compiletest-based test suites test/ ui/ - compile-fail/ debuginfo/ ... diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index 07e582d4d2..a32a4a7fe5 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -15,7 +15,7 @@ fn main() { // check_version warnings are not printed during setup let changelog_suggestion = - if matches!(config.cmd, Subcommand::Setup {..}) { None } else { check_version(&config) }; + if matches!(config.cmd, Subcommand::Setup { .. }) { None } else { check_version(&config) }; // NOTE: Since `./configure` generates a `config.toml`, distro maintainers will see the // changelog warning, not the `x.py setup` message. diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 97f40815b8..6708b27b50 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -194,7 +194,8 @@ def default_build_triple(verbose): # being detected as GNU instead of MSVC. default_encoding = sys.getdefaultencoding() try: - version = subprocess.check_output(["rustc", "--version", "--verbose"]) + version = subprocess.check_output(["rustc", "--version", "--verbose"], + stderr=subprocess.DEVNULL) version = version.decode(default_encoding) host = next(x for x in version.split('\n') if x.startswith("host: ")) triple = host.split("host: ")[1] @@ -351,11 +352,13 @@ def output(filepath): with open(tmp, 'w') as f: yield f try: - os.remove(filepath) # PermissionError/OSError on Win32 if in use - os.rename(tmp, filepath) + if os.path.exists(filepath): + os.remove(filepath) # PermissionError/OSError on Win32 if in use except OSError: shutil.copy2(tmp, filepath) os.remove(tmp) + return + os.rename(tmp, filepath) class RustBuild(object): @@ -391,7 +394,7 @@ class RustBuild(object): if self.rustc().startswith(self.bin_root()) and \ (not os.path.exists(self.rustc()) or - self.program_out_of_date(self.rustc_stamp())): + self.program_out_of_date(self.rustc_stamp(), self.date)): if os.path.exists(self.bin_root()): shutil.rmtree(self.bin_root()) tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' @@ -411,7 +414,7 @@ class RustBuild(object): lib_dir = "{}/lib".format(self.bin_root()) for lib in os.listdir(lib_dir): if lib.endswith(".so"): - self.fix_bin_or_dylib("{}/{}".format(lib_dir, lib)) + self.fix_bin_or_dylib(os.path.join(lib_dir, lib), rpath_libz=True) with output(self.rustc_stamp()) as rust_stamp: rust_stamp.write(self.date) @@ -427,7 +430,7 @@ class RustBuild(object): self.fix_bin_or_dylib("{}/bin/rustfmt".format(self.bin_root())) self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(self.bin_root())) with output(self.rustfmt_stamp()) as rustfmt_stamp: - rustfmt_stamp.write(self.date + self.rustfmt_channel) + rustfmt_stamp.write(self.rustfmt_channel) if self.downloading_llvm(): # We want the most recent LLVM submodule update to avoid downloading @@ -449,17 +452,35 @@ class RustBuild(object): "{}/src/bootstrap/download-ci-llvm-stamp".format(top_level), ]).decode(sys.getdefaultencoding()).strip() llvm_assertions = self.get_toml('assertions', 'llvm') == 'true' + llvm_root = self.llvm_root() + llvm_lib = os.path.join(llvm_root, "lib") if self.program_out_of_date(self.llvm_stamp(), llvm_sha + str(llvm_assertions)): self._download_ci_llvm(llvm_sha, llvm_assertions) for binary in ["llvm-config", "FileCheck"]: - self.fix_bin_or_dylib("{}/bin/{}".format(self.llvm_root(), binary)) + self.fix_bin_or_dylib(os.path.join(llvm_root, "bin", binary), rpath_libz=True) + for lib in os.listdir(llvm_lib): + if lib.endswith(".so"): + self.fix_bin_or_dylib(os.path.join(llvm_lib, lib), rpath_libz=True) with output(self.llvm_stamp()) as llvm_stamp: - llvm_stamp.write(self.date + llvm_sha + str(llvm_assertions)) + llvm_stamp.write(llvm_sha + str(llvm_assertions)) def downloading_llvm(self): opt = self.get_toml('download-ci-llvm', 'llvm') + # This is currently all tier 1 targets (since others may not have CI + # artifacts) + # https://doc.rust-lang.org/rustc/platform-support.html#tier-1 + supported_platforms = [ + "aarch64-unknown-linux-gnu", + "i686-pc-windows-gnu", + "i686-pc-windows-msvc", + "i686-unknown-linux-gnu", + "x86_64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-gnu", + "x86_64-pc-windows-msvc", + ] return opt == "true" \ - or (opt == "if-available" and self.build == "x86_64-unknown-linux-gnu") + or (opt == "if-available" and self.build in supported_platforms) def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None): if date is None: @@ -485,7 +506,12 @@ class RustBuild(object): url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(llvm_sha) if llvm_assertions: url = url.replace('rustc-builds', 'rustc-builds-alt') - tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' + # ci-artifacts are only stored as .xz, not .gz + if not support_xz(): + print("error: XZ support is required to download LLVM") + print("help: consider disabling `download-ci-llvm` or using python3") + exit(1) + tarball_suffix = '.tar.xz' filename = "rust-dev-nightly-" + self.build + tarball_suffix tarball = os.path.join(rustc_cache, filename) if not os.path.exists(tarball): @@ -494,7 +520,7 @@ class RustBuild(object): match="rust-dev", verbose=self.verbose) - def fix_bin_or_dylib(self, fname): + def fix_bin_or_dylib(self, fname, rpath_libz=False): """Modifies the interpreter section of 'fname' to fix the dynamic linker, or the RPATH section, to fix the dynamic library search path @@ -564,20 +590,22 @@ class RustBuild(object): self.nix_deps_dir = nix_deps_dir patchelf = "{}/patchelf/bin/patchelf".format(nix_deps_dir) + patchelf_args = [] - if fname.endswith(".so"): - # Dynamic library, patch RPATH to point to system dependencies. + if rpath_libz: + # Patch RPATH to add `zlib` dependency that stems from LLVM dylib_deps = ["zlib"] rpath_entries = [ # Relative default, all binary and dynamic libraries we ship # appear to have this (even when `../lib` is redundant). "$ORIGIN/../lib", ] + ["{}/{}/lib".format(nix_deps_dir, dep) for dep in dylib_deps] - patchelf_args = ["--set-rpath", ":".join(rpath_entries)] - else: + patchelf_args += ["--set-rpath", ":".join(rpath_entries)] + if not fname.endswith(".so"): + # Finally, set the corret .interp for binaries bintools_dir = "{}/stdenv.cc.bintools".format(nix_deps_dir) with open("{}/nix-support/dynamic-linker".format(bintools_dir)) as dynamic_linker: - patchelf_args = ["--set-interpreter", dynamic_linker.read().rstrip()] + patchelf_args += ["--set-interpreter", dynamic_linker.read().rstrip()] try: subprocess.check_output([patchelf] + patchelf_args + [fname]) @@ -616,12 +644,12 @@ class RustBuild(object): return os.path.join(self.llvm_root(), '.llvm-stamp') - def program_out_of_date(self, stamp_path, extra=""): + def program_out_of_date(self, stamp_path, key): """Check if the given program stamp is out of date""" if not os.path.exists(stamp_path) or self.clean: return True with open(stamp_path, 'r') as stamp: - return (self.date + extra) != stamp.read() + return key != stamp.read() def bin_root(self): """Return the binary root directory @@ -806,6 +834,7 @@ class RustBuild(object): target_linker = self.get_toml("linker", build_section) if target_linker is not None: env["RUSTFLAGS"] += " -C linker=" + target_linker + # cfg(bootstrap): Add `-Wsemicolon_in_expressions_from_macros` after the next beta bump env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes" if self.get_toml("deny-warnings", "rust") != "false": env["RUSTFLAGS"] += " -Dwarnings" @@ -1057,10 +1086,10 @@ def bootstrap(help_triggered): else: build.set_normal_environment() + build.build = args.build or build.build_triple() build.update_submodules() # Fetch/build the bootstrap - build.build = args.build or build.build_triple() build.download_stage0() sys.stdout.flush() build.ensure_vendored() diff --git a/src/bootstrap/bootstrap_test.py b/src/bootstrap/bootstrap_test.py index 0e941e1367..6150711415 100644 --- a/src/bootstrap/bootstrap_test.py +++ b/src/bootstrap/bootstrap_test.py @@ -70,6 +70,7 @@ class ProgramOutOfDate(unittest.TestCase): self.build.build_dir = self.container self.rustc_stamp_path = os.path.join(self.container, "stage0", ".rustc-stamp") + self.key = self.build.date + str(None) def tearDown(self): rmtree(self.container) @@ -78,19 +79,19 @@ class ProgramOutOfDate(unittest.TestCase): """Return True when the stamp file does not exists""" if os.path.exists(self.rustc_stamp_path): os.unlink(self.rustc_stamp_path) - self.assertTrue(self.build.program_out_of_date(self.rustc_stamp_path)) + self.assertTrue(self.build.program_out_of_date(self.rustc_stamp_path, self.key)) def test_dates_are_different(self): """Return True when the dates are different""" with open(self.rustc_stamp_path, "w") as rustc_stamp: - rustc_stamp.write("2017-06-14") - self.assertTrue(self.build.program_out_of_date(self.rustc_stamp_path)) + rustc_stamp.write("2017-06-14None") + self.assertTrue(self.build.program_out_of_date(self.rustc_stamp_path, self.key)) def test_same_dates(self): """Return False both dates match""" with open(self.rustc_stamp_path, "w") as rustc_stamp: - rustc_stamp.write("2017-06-15") - self.assertFalse(self.build.program_out_of_date(self.rustc_stamp_path)) + rustc_stamp.write("2017-06-15None") + self.assertFalse(self.build.program_out_of_date(self.rustc_stamp_path, self.key)) if __name__ == '__main__': diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 0fdafa3938..f1a160250d 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -384,7 +384,6 @@ impl<'a> Builder<'a> { test::ExpandYamlAnchors, test::Tidy, test::Ui, - test::CompileFail, test::RunPassValgrind, test::MirOpt, test::Codegen, @@ -737,10 +736,7 @@ impl<'a> Builder<'a> { if self.config.deny_warnings { cmd.arg("-Dwarnings"); } - // cfg(not(bootstrap)), can be removed on the next beta bump - if compiler.stage != 0 { - cmd.arg("-Znormalize-docs"); - } + cmd.arg("-Znormalize-docs"); // Remove make-related flags that can cause jobserver problems. cmd.env_remove("MAKEFLAGS"); @@ -818,12 +814,22 @@ impl<'a> Builder<'a> { cargo.env("REAL_LIBRARY_PATH", e); } + // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger` + // from out of tree it shouldn't matter, since x.py is only used for + // building in-tree. + let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"]; match self.build.config.color { Color::Always => { cargo.arg("--color=always"); + for log in &color_logs { + cargo.env(log, "always"); + } } Color::Never => { cargo.arg("--color=never"); + for log in &color_logs { + cargo.env(log, "never"); + } } Color::Auto => {} // nothing to do } @@ -1133,10 +1139,18 @@ impl<'a> Builder<'a> { // itself, we skip it by default since we know it's safe to do so in that case. // See https://github.com/rust-lang/rust/issues/79361 for more info on this flag. if target.contains("apple") { - if self.config.rust_run_dsymutil { - rustflags.arg("-Zrun-dsymutil=yes"); + if stage == 0 { + if self.config.rust_run_dsymutil { + rustflags.arg("-Zrun-dsymutil=yes"); + } else { + rustflags.arg("-Zrun-dsymutil=no"); + } } else { - rustflags.arg("-Zrun-dsymutil=no"); + if self.config.rust_run_dsymutil { + rustflags.arg("-Csplit-debuginfo=packed"); + } else { + rustflags.arg("-Csplit-debuginfo=unpacked"); + } } } @@ -1244,6 +1258,12 @@ impl<'a> Builder<'a> { // some code doesn't go through this `rustc` wrapper. lint_flags.push("-Wrust_2018_idioms"); lint_flags.push("-Wunused_lifetimes"); + // cfg(bootstrap): unconditionally enable this warning after the next beta bump + // This is currently disabled for the stage1 libstd, since build scripts + // will end up using the bootstrap compiler (which doesn't yet support this lint) + if compiler.stage != 0 && mode != Mode::Std { + lint_flags.push("-Wsemicolon_in_expressions_from_macros"); + } if self.config.deny_warnings { lint_flags.push("-Dwarnings"); @@ -1538,7 +1558,7 @@ impl Rustflags { fn arg(&mut self, arg: &str) -> &mut Self { assert_eq!(arg.split(' ').count(), 1); if !self.0.is_empty() { - self.0.push_str(" "); + self.0.push(' '); } self.0.push_str(arg); self diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index a367aa5349..885fcfff03 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -146,7 +146,7 @@ mod defaults { // rustdoc tool. assert_eq!( first(builder.cache.all::()), - &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 0 }, target: a },] + &[doc::ErrorIndex { target: a },] ); assert_eq!( first(builder.cache.all::()), @@ -556,7 +556,7 @@ mod dist { // rustdoc tool. assert_eq!( first(builder.cache.all::()), - &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },] + &[doc::ErrorIndex { target: a },] ); assert_eq!( first(builder.cache.all::()), @@ -594,7 +594,7 @@ mod dist { // rustdoc tool. assert_eq!( first(builder.cache.all::()), - &[doc::ErrorIndex { compiler: Compiler { host: a, stage: 1 }, target: a },] + &[doc::ErrorIndex { target: a },] ); assert_eq!( first(builder.cache.all::()), diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 2b82f6c30b..6e65be93fe 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -74,9 +74,9 @@ impl GitInfo { if let Some(ref inner) = self.inner { version.push_str(" ("); version.push_str(&inner.short_sha); - version.push_str(" "); + version.push(' '); version.push_str(&inner.commit_date); - version.push_str(")"); + version.push(')'); } version } diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index f65b2b2c79..6626fead77 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -21,6 +21,16 @@ fn args(builder: &Builder<'_>) -> Vec { } if let Subcommand::Clippy { fix, .. } = builder.config.cmd { + // disable the most spammy clippy lints + let ignored_lints = vec![ + "many_single_char_names", // there are a lot in stdarch + "collapsible_if", + "type_complexity", + "missing_safety_doc", // almost 3K warnings + "too_many_arguments", + "needless_lifetimes", // people want to keep the lifetimes + "wrong_self_convention", + ]; let mut args = vec![]; if fix { #[rustfmt::skip] @@ -33,6 +43,7 @@ fn args(builder: &Builder<'_>) -> Vec { ])); } args.extend(strings(&["--", "--cap-lints", "warn"])); + args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint))); args } else { vec![] @@ -62,7 +73,7 @@ impl Step for Std { fn run(self, builder: &Builder<'_>) { let target = self.target; - let compiler = builder.compiler(0, builder.config.build); + let compiler = builder.compiler(builder.top_stage, builder.config.build); let mut cargo = builder.cargo( compiler, @@ -73,7 +84,10 @@ impl Step for Std { ); std_cargo(builder, target, compiler.stage, &mut cargo); - builder.info(&format!("Checking std artifacts ({} -> {})", &compiler.host, target)); + builder.info(&format!( + "Checking stage{} std artifacts ({} -> {})", + builder.top_stage, &compiler.host, target + )); run_cargo( builder, cargo, @@ -83,9 +97,13 @@ impl Step for Std { true, ); - let libdir = builder.sysroot_libdir(compiler, target); - let hostdir = builder.sysroot_libdir(compiler, compiler.host); - add_to_sysroot(&builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + // We skip populating the sysroot in non-zero stage because that'll lead + // to rlib/rmeta conflicts if std gets built during this session. + if compiler.stage == 0 { + let libdir = builder.sysroot_libdir(compiler, target); + let hostdir = builder.sysroot_libdir(compiler, compiler.host); + add_to_sysroot(&builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + } // Then run cargo again, once we've put the rmeta files for the library // crates into the sysroot. This is needed because e.g., core's tests @@ -113,8 +131,8 @@ impl Step for Std { } builder.info(&format!( - "Checking std test/bench/example targets ({} -> {})", - &compiler.host, target + "Checking stage{} std test/bench/example targets ({} -> {})", + builder.top_stage, &compiler.host, target )); run_cargo( builder, @@ -152,10 +170,20 @@ impl Step for Rustc { /// the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(0, builder.config.build); + let compiler = builder.compiler(builder.top_stage, builder.config.build); let target = self.target; - builder.ensure(Std { target }); + if compiler.stage != 0 { + // If we're not in stage 0, then we won't have a std from the beta + // compiler around. That means we need to make sure there's one in + // the sysroot for the compiler to find. Otherwise, we're going to + // fail when building crates that need to generate code (e.g., build + // scripts and their dependencies). + builder.ensure(crate::compile::Std { target: compiler.host, compiler }); + builder.ensure(crate::compile::Std { target, compiler }); + } else { + builder.ensure(Std { target }); + } let mut cargo = builder.cargo( compiler, @@ -176,7 +204,10 @@ impl Step for Rustc { cargo.arg("-p").arg(krate.name); } - builder.info(&format!("Checking compiler artifacts ({} -> {})", &compiler.host, target)); + builder.info(&format!( + "Checking stage{} compiler artifacts ({} -> {})", + builder.top_stage, &compiler.host, target + )); run_cargo( builder, cargo, @@ -214,7 +245,7 @@ impl Step for CodegenBackend { } fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(0, builder.config.build); + let compiler = builder.compiler(builder.top_stage, builder.config.build); let target = self.target; let backend = self.backend; @@ -233,8 +264,8 @@ impl Step for CodegenBackend { rustc_cargo_env(builder, &mut cargo, target); builder.info(&format!( - "Checking {} artifacts ({} -> {})", - backend, &compiler.host.triple, target.triple + "Checking stage{} {} artifacts ({} -> {})", + builder.top_stage, backend, &compiler.host.triple, target.triple )); run_cargo( @@ -269,7 +300,7 @@ macro_rules! tool_check_step { } fn run(self, builder: &Builder<'_>) { - let compiler = builder.compiler(0, builder.config.build); + let compiler = builder.compiler(builder.top_stage, builder.config.build); let target = self.target; builder.ensure(Rustc { target }); @@ -289,8 +320,16 @@ macro_rules! tool_check_step { cargo.arg("--all-targets"); } + // Enable internal lints for clippy and rustdoc + // NOTE: this intentionally doesn't enable lints for any other tools, + // see https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776 + if $path == "src/tools/rustdoc" || $path == "src/tools/clippy" { + cargo.rustflag("-Zunstable-options"); + } + builder.info(&format!( - "Checking {} artifacts ({} -> {})", + "Checking stage{} {} artifacts ({} -> {})", + builder.top_stage, stringify!($name).to_lowercase(), &compiler.host.triple, target.triple diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs index f83dfe8e63..9b9df36e7d 100644 --- a/src/bootstrap/clean.rs +++ b/src/bootstrap/clean.rs @@ -21,6 +21,7 @@ pub fn clean(build: &Build, all: bool) { } else { rm_rf(&build.out.join("tmp")); rm_rf(&build.out.join("dist")); + rm_rf(&build.out.join("bootstrap")); for host in &build.hosts { let entries = match build.out.join(host.triple).read_dir() { diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index a31c87d431..65a2d210a9 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -7,6 +7,7 @@ //! goes along from the output of the previous stage. use std::borrow::Cow; +use std::collections::HashSet; use std::env; use std::fs; use std::io::prelude::*; @@ -187,14 +188,16 @@ fn copy_self_contained_objects( } } else if target.ends_with("-wasi") { let srcdir = builder.wasi_root(target).unwrap().join("lib/wasm32-wasi"); - copy_and_stamp( - builder, - &libdir_self_contained, - &srcdir, - "crt1.o", - &mut target_deps, - DependencyType::TargetSelfContained, - ); + for &obj in &["crt1.o", "crt1-reactor.o"] { + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir, + obj, + &mut target_deps, + DependencyType::TargetSelfContained, + ); + } } else if target.contains("windows-gnu") { for obj in ["crt2.o", "dllcrt2.o"].iter() { let src = compiler_file(builder, builder.cc(target), target, obj); @@ -355,15 +358,12 @@ fn copy_sanitizers( let dst = libdir.join(&runtime.name); builder.copy(&runtime.path, &dst); - if target == "x86_64-apple-darwin" { - // Update the library install name reflect the fact it has been renamed. - let status = Command::new("install_name_tool") - .arg("-id") - .arg(format!("@rpath/{}", runtime.name)) - .arg(&dst) - .status() - .expect("failed to execute `install_name_tool`"); - assert!(status.success()); + if target == "x86_64-apple-darwin" || target == "aarch64-apple-darwin" { + // Update the library’s install name to reflect that it has has been renamed. + apple_darwin_update_library_name(&dst, &format!("@rpath/{}", &runtime.name)); + // Upon renaming the install name, the code signature of the file will invalidate, + // so we will sign it again. + apple_darwin_sign_file(&dst); } target_deps.push(dst); @@ -372,6 +372,27 @@ fn copy_sanitizers( target_deps } +fn apple_darwin_update_library_name(library_path: &Path, new_name: &str) { + let status = Command::new("install_name_tool") + .arg("-id") + .arg(new_name) + .arg(library_path) + .status() + .expect("failed to execute `install_name_tool`"); + assert!(status.success()); +} + +fn apple_darwin_sign_file(file_path: &Path) { + let status = Command::new("codesign") + .arg("-f") // Force to rewrite the existing signature + .arg("-s") + .arg("-") + .arg(file_path) + .status() + .expect("failed to execute `codesign`"); + assert!(status.success()); +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct StartupObjects { pub compiler: Compiler, @@ -991,13 +1012,26 @@ impl Step for Assemble { builder.info(&format!("Assembling stage{} compiler ({})", stage, host)); // Link in all dylibs to the libdir + let stamp = librustc_stamp(builder, build_compiler, target_compiler.host); + let proc_macros = builder + .read_stamp_file(&stamp) + .into_iter() + .filter_map(|(path, dependency_type)| { + if dependency_type == DependencyType::Host { + Some(path.file_name().unwrap().to_owned().into_string().unwrap()) + } else { + None + } + }) + .collect::>(); + let sysroot = builder.sysroot(target_compiler); let rustc_libdir = builder.rustc_libdir(target_compiler); t!(fs::create_dir_all(&rustc_libdir)); let src_libdir = builder.sysroot_libdir(build_compiler, host); for f in builder.read_dir(&src_libdir) { let filename = f.file_name().into_string().unwrap(); - if is_dylib(&filename) { + if is_dylib(&filename) && !proc_macros.contains(&filename) { builder.copy(&f.path(), &rustc_libdir.join(&filename)); } } @@ -1016,8 +1050,10 @@ impl Step for Assemble { builder.copy(&lld_install.join("bin").join(&src_exe), &libdir_bin.join(&dst_exe)); } - // Similarly, copy `llvm-dwp` into libdir for Split DWARF. - { + // Similarly, copy `llvm-dwp` into libdir for Split DWARF. Only copy it when the LLVM + // backend is used to avoid unnecessarily building LLVM and because LLVM is not checked + // out by default when the LLVM backend is not enabled. + if builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) { let src_exe = exe("llvm-dwp", target_compiler.host); let dst_exe = exe("rust-llvm-dwp", target_compiler.host); let llvm_config_bin = builder.ensure(native::Llvm { target: target_compiler.host }); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index def8f21543..ec1308ab82 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -148,6 +148,7 @@ pub struct Config { pub dist_sign_folder: Option, pub dist_upload_addr: Option, pub dist_gpg_password_file: Option, + pub dist_compression_formats: Option>, // libstd features pub backtrace: bool, // support for RUST_BACKTRACE @@ -334,7 +335,7 @@ impl Merge for TomlConfig { *x = Some(new); } } - }; + } do_merge(&mut self.build, build); do_merge(&mut self.install, install); do_merge(&mut self.llvm, llvm); @@ -376,6 +377,7 @@ struct Build { configure_args: Option>, local_rebuild: Option, print_step_timings: Option, + check_stage: Option, doc_stage: Option, build_stage: Option, test_stage: Option, @@ -438,6 +440,7 @@ struct Dist { upload_addr: Option, src_tarball: Option, missing_tools: Option, + compression_formats: Option>, } #[derive(Deserialize)] @@ -674,6 +677,7 @@ impl Config { // See https://github.com/rust-lang/compiler-team/issues/326 config.stage = match config.cmd { + Subcommand::Check { .. } => flags.stage.or(build.check_stage).unwrap_or(0), Subcommand::Doc { .. } => flags.stage.or(build.doc_stage).unwrap_or(0), Subcommand::Build { .. } => flags.stage.or(build.build_stage).unwrap_or(1), Subcommand::Test { .. } => flags.stage.or(build.test_stage).unwrap_or(1), @@ -683,7 +687,6 @@ impl Config { // These are all bootstrap tools, which don't depend on the compiler. // The stage we pass shouldn't matter, but use 0 just in case. Subcommand::Clean { .. } - | Subcommand::Check { .. } | Subcommand::Clippy { .. } | Subcommand::Fix { .. } | Subcommand::Run { .. } @@ -814,8 +817,10 @@ impl Config { check_ci_llvm!(llvm.allow_old_toolchain); check_ci_llvm!(llvm.polly); - // CI-built LLVM is shared - config.llvm_link_shared = true; + // CI-built LLVM can be either dynamic or static. + let ci_llvm = config.out.join(&*config.build.triple).join("ci-llvm"); + let link_type = t!(std::fs::read_to_string(ci_llvm.join("link-type.txt"))); + config.llvm_link_shared = link_type == "dynamic"; } if config.llvm_thin_lto { @@ -936,6 +941,7 @@ impl Config { config.dist_sign_folder = t.sign_folder.map(PathBuf::from); config.dist_gpg_password_file = t.gpg_password_file.map(PathBuf::from); config.dist_upload_addr = t.upload_addr; + config.dist_compression_formats = t.compression_formats; set(&mut config.rust_dist_src, t.src_tarball); set(&mut config.missing_tools, t.missing_tools); } diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 42f00ce962..2cabaee68e 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -147,6 +147,8 @@ v("experimental-targets", "llvm.experimental-targets", "experimental LLVM targets to build") v("release-channel", "rust.channel", "the name of the release channel to build") v("release-description", "rust.description", "optional descriptive string for version output") +v("dist-compression-formats", None, + "comma-separated list of compression formats to use") # Used on systems where "cc" is unavailable v("default-linker", "rust.default-linker", "the default linker") @@ -349,6 +351,8 @@ for key in known_args: elif option.name == 'option-checking': # this was handled above pass + elif option.name == 'dist-compression-formats': + set('dist.compression-formats', value.split(',')) else: raise RuntimeError("unhandled option {}".format(option.name)) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 871a175091..86c84a2a50 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -1332,17 +1332,17 @@ impl Step for Extended { license += &builder.read(&builder.src.join("COPYRIGHT")); license += &builder.read(&builder.src.join("LICENSE-APACHE")); license += &builder.read(&builder.src.join("LICENSE-MIT")); - license.push_str("\n"); - license.push_str("\n"); + license.push('\n'); + license.push('\n'); let rtf = r"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}}\nowwrap\fs18"; let mut rtf = rtf.to_string(); - rtf.push_str("\n"); + rtf.push('\n'); for line in license.lines() { rtf.push_str(line); rtf.push_str("\\line "); } - rtf.push_str("}"); + rtf.push('}'); fn filter(contents: &str, marker: &str) -> String { let start = format!("tool-{}-start", marker); @@ -1806,19 +1806,11 @@ fn add_env(builder: &Builder<'_>, cmd: &mut Command, target: TargetSelection) { } } -/// Maybe add libLLVM.so to the given destination lib-dir. It will only have -/// been built if LLVM tools are linked dynamically. +/// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking. /// -/// Note: This function does not yet support Windows, but we also don't support -/// linking LLVM tools dynamically on Windows yet. -fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir: &Path) { - if !builder.config.llvm_link_shared { - // We do not need to copy LLVM files into the sysroot if it is not - // dynamically linked; it is already included into librustc_llvm - // statically. - return; - } +/// Returns whether the files were actually copied. +fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir: &Path) -> bool { if let Some(config) = builder.config.target_config.get(&target) { if config.llvm_config.is_some() && !builder.config.llvm_from_ci { // If the LLVM was externally provided, then we don't currently copy @@ -1834,7 +1826,7 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir // // If the LLVM is coming from ourselves (just from CI) though, we // still want to install it, as it otherwise won't be available. - return; + return false; } } @@ -1843,31 +1835,48 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir // clear why this is the case, though. llvm-config will emit the versioned // paths and we don't want those in the sysroot (as we're expecting // unversioned paths). - if target.contains("apple-darwin") { + if target.contains("apple-darwin") && builder.config.llvm_link_shared { let src_libdir = builder.llvm_out(target).join("lib"); let llvm_dylib_path = src_libdir.join("libLLVM.dylib"); if llvm_dylib_path.exists() { builder.install(&llvm_dylib_path, dst_libdir, 0o644); } + !builder.config.dry_run } else if let Ok(llvm_config) = crate::native::prebuilt_llvm_config(builder, target) { - let files = output(Command::new(llvm_config).arg("--libfiles")); - for file in files.lines() { + let mut cmd = Command::new(llvm_config); + cmd.arg("--libfiles"); + builder.verbose(&format!("running {:?}", cmd)); + let files = output(&mut cmd); + for file in files.trim_end().split(' ') { builder.install(Path::new(file), dst_libdir, 0o644); } + !builder.config.dry_run + } else { + false } } /// Maybe add libLLVM.so to the target lib-dir for linking. pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) { let dst_libdir = sysroot.join("lib/rustlib").join(&*target.triple).join("lib"); - maybe_install_llvm(builder, target, &dst_libdir); + // We do not need to copy LLVM files into the sysroot if it is not + // dynamically linked; it is already included into librustc_llvm + // statically. + if builder.config.llvm_link_shared { + maybe_install_llvm(builder, target, &dst_libdir); + } } /// Maybe add libLLVM.so to the runtime lib-dir for rustc itself. pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection, sysroot: &Path) { let dst_libdir = sysroot.join(builder.sysroot_libdir_relative(Compiler { stage: 1, host: target })); - maybe_install_llvm(builder, target, &dst_libdir); + // We do not need to copy LLVM files into the sysroot if it is not + // dynamically linked; it is already included into librustc_llvm + // statically. + if builder.config.llvm_link_shared { + maybe_install_llvm(builder, target, &dst_libdir); + } } #[derive(Clone, Debug, Eq, Hash, PartialEq)] @@ -1979,7 +1988,10 @@ impl Step for RustDev { // `$ORIGIN/../lib` can find it. It may also be used as a dependency // of `rustc-dev` to support the inherited `-lLLVM` when using the // compiler libraries. - maybe_install_llvm(builder, target, &tarball.image_dir().join("lib")); + let dst_libdir = tarball.image_dir().join("lib"); + maybe_install_llvm(builder, target, &dst_libdir); + let link_type = if builder.config.llvm_link_shared { "dynamic" } else { "static" }; + t!(std::fs::write(tarball.image_dir().join("link-type.txt"), link_type), dst_libdir); Some(tarball.generate()) } diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 8cacc2512e..c4b3e4cf95 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -500,18 +500,17 @@ impl Step for Rustc { let target = self.target; builder.info(&format!("Documenting stage{} compiler ({})", stage, target)); - // This is the intended out directory for compiler documentation. - let out = builder.compiler_doc_out(target); - t!(fs::create_dir_all(&out)); - - let compiler = builder.compiler(stage, builder.config.build); - if !builder.config.compiler_docs { builder.info("\tskipping - compiler/librustdoc docs disabled"); return; } + // This is the intended out directory for compiler documentation. + let out = builder.compiler_doc_out(target); + t!(fs::create_dir_all(&out)); + // Build rustc. + let compiler = builder.compiler(stage, builder.config.build); builder.ensure(compile::Rustc { compiler, target }); // This uses a shared directory so that librustdoc documentation gets @@ -521,16 +520,17 @@ impl Step for Rustc { // merging the search index, or generating local (relative) links. let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc"); t!(symlink_dir_force(&builder.config, &out, &out_dir)); + // Cargo puts proc macros in `target/doc` even if you pass `--target` + // explicitly (https://github.com/rust-lang/cargo/issues/7677). + let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc"); + t!(symlink_dir_force(&builder.config, &out, &proc_macro_out_dir)); // Build cargo command. let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc"); cargo.rustdocflag("--document-private-items"); cargo.rustdocflag("--enable-index-page"); cargo.rustdocflag("-Zunstable-options"); - // cfg(not(bootstrap)), can be removed on the next beta bump - if stage != 0 { - cargo.rustdocflag("-Znormalize-docs"); - } + cargo.rustdocflag("-Znormalize-docs"); compile::rustc_cargo(builder, &mut cargo, target); // Only include compiler crates, no dependencies of those, such as `libc`. @@ -628,13 +628,14 @@ impl Step for Rustdoc { cargo.arg("-p").arg("rustdoc"); cargo.rustdocflag("--document-private-items"); + cargo.rustdocflag("--enable-index-page"); + cargo.rustdocflag("-Zunstable-options"); builder.run(&mut cargo.into()); } } #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct ErrorIndex { - pub compiler: Compiler, pub target: TargetSelection, } @@ -650,12 +651,7 @@ impl Step for ErrorIndex { fn make_run(run: RunConfig<'_>) { let target = run.target; - // error_index_generator depends on librustdoc. Use the compiler that - // is normally used to build rustdoc for other documentation so that - // it shares the same artifacts. - let compiler = - run.builder.compiler_for(run.builder.top_stage, run.builder.config.build, target); - run.builder.ensure(ErrorIndex { compiler, target }); + run.builder.ensure(ErrorIndex { target }); } /// Generates the HTML rendered error-index by running the @@ -664,7 +660,7 @@ impl Step for ErrorIndex { builder.info(&format!("Documenting error index ({})", self.target)); let out = builder.doc_out(self.target); t!(fs::create_dir_all(&out)); - let mut index = tool::ErrorIndex::command(builder, self.compiler); + let mut index = tool::ErrorIndex::command(builder); index.arg("html"); index.arg(out.join("error-index.html")); index.arg(&builder.version); diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index b29ecd6540..fb5b058cb4 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/80087 +Last change is for: https://github.com/rust-lang/rust/pull/80932 diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index d6a45f1c17..55062e11e0 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -614,14 +614,10 @@ Arguments: }; if let Subcommand::Check { .. } = &cmd { - if matches.opt_str("stage").is_some() { - println!("--stage not supported for x.py check, always treated as stage 0"); - process::exit(1); - } if matches.opt_str("keep-stage").is_some() || matches.opt_str("keep-stage-std").is_some() { - println!("--keep-stage not supported for x.py check, only one stage available"); + println!("--keep-stage not yet supported for x.py check"); process::exit(1); } } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index a47ddfbcc1..88fdcfa2d4 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -1083,7 +1083,7 @@ impl Build { if let Some(ref s) = self.config.description { version.push_str(" ("); version.push_str(s); - version.push_str(")"); + version.push(')'); } version } @@ -1144,7 +1144,7 @@ impl Build { && (dep != "profiler_builtins" || target .map(|t| self.config.profiler_enabled(t)) - .unwrap_or(self.config.any_profiler_enabled())) + .unwrap_or_else(|| self.config.any_profiler_enabled())) && (dep != "rustc_codegen_llvm" || self.config.llvm_enabled()) { list.push(*dep); diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 1564cfb061..fd39944e17 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -66,7 +66,6 @@ check-stage2-T-x86_64-unknown-linux-musl-H-x86_64-unknown-linux-gnu: TESTS_IN_2 := \ src/test/ui \ - src/test/compile-fail \ src/tools/linkchecker ci-subset-1: @@ -75,8 +74,7 @@ ci-subset-2: $(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_2) TESTS_IN_MINGW_2 := \ - src/test/ui \ - src/test/compile-fail + src/test/ui ci-mingw-subset-1: $(Q)$(BOOTSTRAP) test --stage 2 $(TESTS_IN_MINGW_2:%=--exclude %) diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index d716b23af6..609ac8b366 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -171,7 +171,6 @@ impl Step for Llvm { .define("LLVM_TARGETS_TO_BUILD", llvm_targets) .define("LLVM_EXPERIMENTAL_TARGETS_TO_BUILD", llvm_exp_targets) .define("LLVM_INCLUDE_EXAMPLES", "OFF") - .define("LLVM_INCLUDE_TESTS", "OFF") .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") .define("LLVM_ENABLE_TERMINFO", "OFF") @@ -802,6 +801,7 @@ fn supported_sanitizers( }; match &*target.triple { + "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "aarch64-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), "aarch64-unknown-linux-gnu" => { common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan"]) diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index acb941d954..08acc3d671 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -163,7 +163,11 @@ pub fn check(build: &mut Build) { panic!("the iOS target is only supported on macOS"); } - build.config.target_config.entry(*target).or_insert(Target::from_triple(&target.triple)); + build + .config + .target_config + .entry(*target) + .or_insert_with(|| Target::from_triple(&target.triple)); if target.contains("-none-") || target.contains("nvptx") { if build.no_std(*target) == Some(false) { diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 2d4484c562..725147767d 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -89,7 +89,7 @@ pub fn setup(src_path: &Path, profile: Profile) { std::process::exit(1); } - let path = cfg_file.unwrap_or("config.toml".into()); + let path = cfg_file.unwrap_or_else(|| "config.toml".into()); let settings = format!( "# Includes one of the default files in src/bootstrap/defaults\n\ profile = \"{}\"\n\ @@ -156,7 +156,7 @@ pub fn interactive_path() -> io::Result { io::stdout().flush()?; let mut input = String::new(); io::stdin().read_line(&mut input)?; - if input == "" { + if input.is_empty() { eprintln!("EOF on stdin, when expecting answer to question. Giving up."); std::process::exit(1); } diff --git a/src/bootstrap/tarball.rs b/src/bootstrap/tarball.rs index 9cfe12359d..7fb03056f1 100644 --- a/src/bootstrap/tarball.rs +++ b/src/bootstrap/tarball.rs @@ -111,7 +111,7 @@ impl<'a> Tarball<'a> { fn new_inner(builder: &'a Builder<'a>, component: &str, target: Option) -> Self { let pkgname = crate::dist::pkgname(builder, component); - let mut temp_dir = builder.out.join("tmp").join("tarball"); + let mut temp_dir = builder.out.join("tmp").join("tarball").join(component); if let Some(target) = &target { temp_dir = temp_dir.join(target); } @@ -287,10 +287,24 @@ impl<'a> Tarball<'a> { build_cli(&self, &mut cmd); cmd.arg("--work-dir").arg(&self.temp_dir); + if let Some(formats) = &self.builder.config.dist_compression_formats { + assert!(!formats.is_empty(), "dist.compression-formats can't be empty"); + cmd.arg("--compression-formats").arg(formats.join(",")); + } self.builder.run(&mut cmd); + // Use either the first compression format defined, or "gz" as the default. + let ext = self + .builder + .config + .dist_compression_formats + .as_ref() + .and_then(|formats| formats.get(0)) + .map(|s| s.as_str()) + .unwrap_or("gz"); + GeneratedTarball { - path: crate::dist::distdir(self.builder).join(format!("{}.tar.gz", package_name)), + path: crate::dist::distdir(self.builder).join(format!("{}.tar.{}", package_name, ext)), decompressed_output: self.temp_dir.join(package_name), work: self.temp_dir, } diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index ce4274fa22..d9132f20d8 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -14,7 +14,7 @@ use std::process::Command; use build_helper::{self, output, t}; use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step}; -use crate::cache::{Interned, INTERNER}; +use crate::cache::Interned; use crate::compile; use crate::config::TargetSelection; use crate::dist; @@ -869,12 +869,6 @@ default_test_with_compare_mode!(Ui { compare_mode: "nll" }); -default_test!(CompileFail { - path: "src/test/compile-fail", - mode: "compile-fail", - suite: "compile-fail" -}); - default_test!(RunPassValgrind { path: "src/test/run-pass-valgrind", mode: "run-pass-valgrind", @@ -1017,6 +1011,13 @@ note: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--rustdoc-path").arg(builder.rustdoc(compiler)); } + if mode == "rustdoc-json" { + // Use the beta compiler for jsondocck + let json_compiler = compiler.with_stage(0); + cmd.arg("--jsondocck-path") + .arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target })); + } + if mode == "run-make" && suite.ends_with("fulldeps") { cmd.arg("--rust-demangler-path").arg(builder.tool_exe(Tool::RustDemangler)); } @@ -1132,7 +1133,19 @@ note: if you're sure you want to do this, please open an issue as to why. In the Ok(path) => path, Err(_) => p, }) - .filter(|p| p.starts_with(suite_path) && (p.is_dir() || p.is_file())) + .filter(|p| p.starts_with(suite_path)) + .filter(|p| { + let exists = p.is_dir() || p.is_file(); + if !exists { + if let Some(p) = p.to_str() { + builder.info(&format!( + "Warning: Skipping \"{}\": not a regular file or directory", + p + )); + } + } + exists + }) .filter_map(|p| { // Since test suite paths are themselves directories, if we don't // specify a directory or file, we'll get an empty string here @@ -1141,7 +1154,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the // flag is respected, so providing an empty --test-args conflicts with // any following it. match p.strip_prefix(suite_path).ok().and_then(|p| p.to_str()) { - Some(s) if s != "" => Some(s), + Some(s) if !s.is_empty() => Some(s), _ => None, } }) @@ -1469,7 +1482,7 @@ impl Step for ErrorIndex { // error_index_generator depends on librustdoc. Use the compiler that // is normally used to build rustdoc for other tests (like compiletest // tests in src/test/rustdoc) so that it shares the same artifacts. - let compiler = run.builder.compiler_for(run.builder.top_stage, run.target, run.target); + let compiler = run.builder.compiler(run.builder.top_stage, run.builder.config.build); run.builder.ensure(ErrorIndex { compiler }); } @@ -1486,19 +1499,16 @@ impl Step for ErrorIndex { t!(fs::create_dir_all(&dir)); let output = dir.join("error-index.md"); - let mut tool = tool::ErrorIndex::command(builder, compiler); + let mut tool = tool::ErrorIndex::command(builder); tool.arg("markdown").arg(&output); - // Use the rustdoc that was built by self.compiler. This copy of - // rustdoc is shared with other tests (like compiletest tests in - // src/test/rustdoc). This helps avoid building rustdoc multiple - // times. - let rustdoc_compiler = builder.compiler(builder.top_stage, builder.config.build); - builder.info(&format!("Testing error-index stage{}", rustdoc_compiler.stage)); + builder.info(&format!("Testing error-index stage{}", compiler.stage)); let _time = util::timeit(&builder); builder.run_quiet(&mut tool); - builder.ensure(compile::Std { compiler: rustdoc_compiler, target: rustdoc_compiler.host }); - markdown_test(builder, rustdoc_compiler, &output); + // The tests themselves need to link to std, so make sure it is + // available. + builder.ensure(compile::Std { compiler, target: compiler.host }); + markdown_test(builder, compiler, &output); } } @@ -1600,55 +1610,6 @@ impl Step for CrateLibrustc { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct CrateNotDefault { - compiler: Compiler, - target: TargetSelection, - test_kind: TestKind, - krate: &'static str, -} - -impl Step for CrateNotDefault { - type Output = (); - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/librustc_asan") - .path("src/librustc_lsan") - .path("src/librustc_msan") - .path("src/librustc_tsan") - } - - fn make_run(run: RunConfig<'_>) { - let builder = run.builder; - let compiler = builder.compiler(builder.top_stage, run.build_triple()); - - let test_kind = builder.kind.into(); - - builder.ensure(CrateNotDefault { - compiler, - target: run.target, - test_kind, - krate: match run.path { - _ if run.path.ends_with("src/librustc_asan") => "rustc_asan", - _ if run.path.ends_with("src/librustc_lsan") => "rustc_lsan", - _ if run.path.ends_with("src/librustc_msan") => "rustc_msan", - _ if run.path.ends_with("src/librustc_tsan") => "rustc_tsan", - _ => panic!("unexpected path {:?}", run.path), - }, - }); - } - - fn run(self, builder: &Builder<'_>) { - builder.ensure(Crate { - compiler: self.compiler, - target: self.target, - mode: Mode::Std, - test_kind: self.test_kind, - krate: INTERNER.intern_str(self.krate), - }); - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Crate { pub compiler: Compiler, @@ -1919,8 +1880,7 @@ impl Step for RemoteCopyLibs { builder.info(&format!("REMOTE copy libs to emulator ({})", target)); t!(fs::create_dir_all(builder.out.join("tmp"))); - let server = - builder.ensure(tool::RemoteTestServer { compiler: compiler.with_stage(0), target }); + let server = builder.ensure(tool::RemoteTestServer { compiler, target }); // Spawn the emulator and wait for it to come online let tool = builder.tool_exe(Tool::RemoteTestClient); diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index dc786249d9..bf6bea539e 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -367,6 +367,7 @@ bootstrap_tool!( RustdocTheme, "src/tools/rustdoc-themes", "rustdoc-themes"; ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors"; LintDocs, "src/tools/lint-docs", "lint-docs"; + JsonDocCk, "src/tools/jsondocck", "jsondocck"; ); #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] @@ -375,7 +376,15 @@ pub struct ErrorIndex { } impl ErrorIndex { - pub fn command(builder: &Builder<'_>, compiler: Compiler) -> Command { + pub fn command(builder: &Builder<'_>) -> Command { + // This uses stage-1 to match the behavior of building rustdoc. + // Error-index-generator links with the rustdoc library, so we want to + // use the same librustdoc to avoid building rustdoc twice (and to + // avoid building the compiler an extra time). This uses + // saturating_sub to deal with building with stage 0. (Using stage 0 + // isn't recommended, since it will fail if any new error index tests + // use new syntax, but it should work otherwise.) + let compiler = builder.compiler(builder.top_stage.saturating_sub(1), builder.config.build); let mut cmd = Command::new(builder.ensure(ErrorIndex { compiler })); add_dylib_path( vec![PathBuf::from(&builder.sysroot_libdir(compiler, compiler.host))], @@ -395,8 +404,14 @@ impl Step for ErrorIndex { fn make_run(run: RunConfig<'_>) { // Compile the error-index in the same stage as rustdoc to avoid // recompiling rustdoc twice if we can. - let host = run.builder.config.build; - let compiler = run.builder.compiler_for(run.builder.top_stage, host, host); + // + // NOTE: This `make_run` isn't used in normal situations, only if you + // manually build the tool with `x.py build + // src/tools/error-index-generator` which almost nobody does. + // Normally, `x.py test` or `x.py doc` will use the + // `ErrorIndex::command` function instead. + let compiler = + run.builder.compiler(run.builder.top_stage.saturating_sub(1), run.builder.config.build); run.builder.ensure(ErrorIndex { compiler }); } diff --git a/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile b/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile index 9370f5debb..8a91859379 100644 --- a/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile @@ -1,6 +1,6 @@ -FROM ubuntu:16.04 +FROM ubuntu:20.04 -RUN apt-get update -y && apt-get install -y --no-install-recommends \ +RUN apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ bc \ bzip2 \ ca-certificates \ @@ -33,32 +33,32 @@ WORKDIR /build # the kernel. This file was generated by running `make vexpress_defconfig` # followed by `make menuconfig` and then enabling the IPv6 protocol page. COPY host-x86_64/armhf-gnu/vexpress_config /build/.config -RUN curl https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.42.tar.xz | \ +RUN curl https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.253.tar.xz | \ tar xJf - && \ - cd /build/linux-4.4.42 && \ + cd /build/linux-4.4.253 && \ cp /build/.config . && \ make -j$(nproc) all && \ cp arch/arm/boot/zImage /tmp && \ cd /build && \ - rm -rf linux-4.4.42 + rm -rf linux-4.4.253 # Compile an instance of busybox as this provides a lightweight system and init # binary which we will boot into. Only trick here is configuring busybox to # build static binaries. -RUN curl https://www.busybox.net/downloads/busybox-1.21.1.tar.bz2 | tar xjf - && \ - cd busybox-1.21.1 && \ +RUN curl https://www.busybox.net/downloads/busybox-1.32.1.tar.bz2 | tar xjf - && \ + cd busybox-1.32.1 && \ make defconfig && \ sed -i 's/.*CONFIG_STATIC.*/CONFIG_STATIC=y/' .config && \ make -j$(nproc) && \ make install && \ mv _install /tmp/rootfs && \ cd /build && \ - rm -rf busybox-1.12.1 + rm -rf busybox-1.32.1 # Download the ubuntu rootfs, which we'll use as a chroot for all our tests. WORKDIR /tmp RUN mkdir rootfs/ubuntu -RUN curl http://cdimage.ubuntu.com/ubuntu-base/releases/16.04/release/ubuntu-base-16.04.6-base-armhf.tar.gz | \ +RUN curl http://cdimage.ubuntu.com/ubuntu-base/releases/20.04/release/ubuntu-base-20.04.1-base-armhf.tar.gz | \ tar xzf - -C rootfs/ubuntu && \ cd rootfs && mkdir proc sys dev etc etc/init.d diff --git a/src/ci/docker/host-x86_64/armhf-gnu/vexpress_config b/src/ci/docker/host-x86_64/armhf-gnu/vexpress_config index 35835cff35..b39e5dcf38 100644 --- a/src/ci/docker/host-x86_64/armhf-gnu/vexpress_config +++ b/src/ci/docker/host-x86_64/armhf-gnu/vexpress_config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 4.4.42 Kernel Configuration +# Linux/arm 4.4.253 Kernel Configuration # CONFIG_ARM=y CONFIG_ARM_HAS_SG_CHAIN=y @@ -60,6 +60,7 @@ CONFIG_HAVE_ARCH_AUDITSYSCALL=y CONFIG_GENERIC_IRQ_PROBE=y CONFIG_GENERIC_IRQ_SHOW=y CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_IRQ_MIGRATION=y CONFIG_HARDIRQS_SW_RESEND=y CONFIG_IRQ_DOMAIN=y CONFIG_IRQ_DOMAIN_HIERARCHY=y @@ -136,6 +137,7 @@ CONFIG_RD_LZMA=y CONFIG_RD_XZ=y CONFIG_RD_LZO=y CONFIG_RD_LZ4=y +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SYSCTL=y CONFIG_ANON_INODES=y @@ -389,6 +391,8 @@ CONFIG_SWP_EMULATE=y # CONFIG_CPU_BIG_ENDIAN is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_CPU_SPECTRE=y +CONFIG_HARDEN_BRANCH_PREDICTOR=y CONFIG_KUSER_HELPERS=y CONFIG_VDSO=y CONFIG_OUTER_CACHE=y @@ -611,7 +615,7 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_IP_PNP_RARP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE_DEMUX is not set -CONFIG_NET_IP_TUNNEL=m +CONFIG_NET_IP_TUNNEL=y # CONFIG_SYN_COOKIES is not set # CONFIG_NET_IPVTI is not set # CONFIG_NET_UDP_TUNNEL is not set @@ -621,7 +625,7 @@ CONFIG_NET_IP_TUNNEL=m # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set # CONFIG_INET_XFRM_TUNNEL is not set -CONFIG_INET_TUNNEL=m +CONFIG_INET_TUNNEL=y CONFIG_INET_XFRM_MODE_TRANSPORT=y CONFIG_INET_XFRM_MODE_TUNNEL=y CONFIG_INET_XFRM_MODE_BEET=y @@ -643,12 +647,12 @@ CONFIG_IPV6=y # CONFIG_IPV6_ILA is not set # CONFIG_INET6_XFRM_TUNNEL is not set # CONFIG_INET6_TUNNEL is not set -CONFIG_INET6_XFRM_MODE_TRANSPORT=m -CONFIG_INET6_XFRM_MODE_TUNNEL=m -CONFIG_INET6_XFRM_MODE_BEET=m +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y # CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set # CONFIG_IPV6_VTI is not set -CONFIG_IPV6_SIT=m +CONFIG_IPV6_SIT=y # CONFIG_IPV6_SIT_6RD is not set CONFIG_IPV6_NDISC_NODETYPE=y # CONFIG_IPV6_TUNNEL is not set @@ -667,6 +671,7 @@ CONFIG_IPV6_NDISC_NODETYPE=y # CONFIG_L2TP is not set # CONFIG_BRIDGE is not set CONFIG_HAVE_NET_DSA=y +# CONFIG_NET_DSA is not set # CONFIG_VLAN_8021Q is not set # CONFIG_DECNET is not set # CONFIG_LLC2 is not set @@ -682,7 +687,6 @@ CONFIG_HAVE_NET_DSA=y # CONFIG_BATMAN_ADV is not set # CONFIG_OPENVSWITCH is not set # CONFIG_VSOCKETS is not set -# CONFIG_NETLINK_MMAP is not set # CONFIG_NETLINK_DIAG is not set # CONFIG_MPLS is not set # CONFIG_HSR is not set @@ -718,6 +722,7 @@ CONFIG_NET_9P_VIRTIO=y # CONFIG_CEPH_LIB is not set # CONFIG_NFC is not set # CONFIG_LWTUNNEL is not set +CONFIG_DST_CACHE=y CONFIG_HAVE_BPF_JIT=y # @@ -1267,6 +1272,7 @@ CONFIG_LEGACY_PTY_COUNT=16 # CONFIG_SERIAL_NONSTANDARD is not set # CONFIG_N_GSM is not set # CONFIG_TRACE_SINK is not set +CONFIG_LDISC_AUTOLOAD=y CONFIG_DEVMEM=y CONFIG_DEVKMEM=y @@ -1304,7 +1310,6 @@ CONFIG_VIRTIO_CONSOLE=y CONFIG_HW_RANDOM=y # CONFIG_HW_RANDOM_TIMERIOMEM is not set CONFIG_HW_RANDOM_VIRTIO=y -# CONFIG_R3964 is not set # CONFIG_RAW_DRIVER is not set # CONFIG_TCG_TPM is not set # CONFIG_XILLYBUS is not set @@ -2009,7 +2014,6 @@ CONFIG_USB_ISP1760_HOST_ROLE=y # CONFIG_USB_EMI26 is not set # CONFIG_USB_ADUTUX is not set # CONFIG_USB_SEVSEG is not set -# CONFIG_USB_RIO500 is not set # CONFIG_USB_LEGOTOWER is not set # CONFIG_USB_LCD is not set # CONFIG_USB_LED is not set @@ -2318,8 +2322,6 @@ CONFIG_ARM_PMU=y # CONFIG_ANDROID is not set # CONFIG_NVMEM is not set # CONFIG_STM is not set -# CONFIG_STM_DUMMY is not set -# CONFIG_STM_SOURCE_CONSOLE is not set # CONFIG_INTEL_TH is not set # @@ -2332,6 +2334,7 @@ CONFIG_ARM_PMU=y # CONFIG_ARM_PSCI_FW=y # CONFIG_FIRMWARE_MEMMAP is not set +CONFIG_HAVE_ARM_SMCCC=y # # File systems @@ -2676,6 +2679,7 @@ CONFIG_TRACING_EVENTS_GPIO=y # CONFIG_TEST_KSTRTOX is not set # CONFIG_TEST_PRINTF is not set # CONFIG_TEST_RHASHTABLE is not set +# CONFIG_TEST_HASH is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_TEST_LKM is not set # CONFIG_TEST_USER_COPY is not set diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index 104b608529..4906f183b4 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -1,6 +1,6 @@ -FROM ubuntu:16.04 +FROM ubuntu:20.04 -RUN apt-get update && apt-get install -y --no-install-recommends \ +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ g++ \ automake \ bison \ @@ -30,6 +30,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ g++-aarch64-linux-gnu \ g++-mips64-linux-gnuabi64 \ g++-mips64el-linux-gnuabi64 \ + gcc-arm-none-eabi \ gcc-sparc64-linux-gnu \ libc6-dev-sparc64-cross \ bzip2 \ @@ -43,11 +44,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ WORKDIR /build -# Use the team-gcc-arm-embedded PPA for a newer version of Arm GCC -RUN add-apt-repository ppa:team-gcc-arm-embedded/ppa && \ - apt-get update && \ - apt-get install -y --no-install-recommends gcc-arm-embedded - COPY host-x86_64/dist-various-1/build-rumprun.sh /build RUN ./build-rumprun.sh diff --git a/src/ci/docker/host-x86_64/dist-various-1/build-rumprun.sh b/src/ci/docker/host-x86_64/dist-various-1/build-rumprun.sh index 103dbbe6fd..bee2d7a476 100755 --- a/src/ci/docker/host-x86_64/dist-various-1/build-rumprun.sh +++ b/src/ci/docker/host-x86_64/dist-various-1/build-rumprun.sh @@ -20,9 +20,10 @@ exit 1 git clone https://github.com/rumpkernel/rumprun cd rumprun -git reset --hard 39a97f37a85e44c69b662f6b97b688fbe892603b +git reset --hard b04d42225a12a6fae57a78a9c1cf23642e46cd00 git submodule update --init -CC=cc hide_output ./build-rr.sh -d /usr/local hw +# Disable -Werror, to avoid breaking the build with newer compilers. +CC=cc NOGCCERROR=1 hide_output ./build-rr.sh -d /usr/local hw cd .. rm -rf rumprun diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index b8b81ab327..6cfacc3b8c 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:18.04 +FROM ubuntu:20.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh @@ -9,12 +9,13 @@ RUN sed -i 's/^# deb-src/deb-src/' /etc/apt/sources.list RUN apt-get update && apt-get build-dep -y clang llvm && apt-get install -y --no-install-recommends \ build-essential \ # gcc-multilib can not be installed together with gcc-arm-linux-gnueabi - gcc-7-multilib \ + g++-8-multilib \ libedit-dev \ libgmp-dev \ libisl-dev \ libmpc-dev \ libmpfr-dev \ + libtinfo5 \ ninja-build \ nodejs \ python3-dev \ @@ -23,7 +24,7 @@ RUN apt-get update && apt-get build-dep -y clang llvm && apt-get install -y --no # Needed for apt-key to work: dirmngr \ gpg-agent \ - g++-7-arm-linux-gnueabi + g++-8-arm-linux-gnueabi RUN apt-key adv --batch --yes --keyserver keyserver.ubuntu.com --recv-keys 74DA7924C5513486 RUN add-apt-repository -y 'deb http://apt.dilos.org/dilos dilos2 main' @@ -41,8 +42,8 @@ ENV \ AR_x86_64_sun_solaris=x86_64-sun-solaris2.10-ar \ CC_x86_64_sun_solaris=x86_64-sun-solaris2.10-gcc \ CXX_x86_64_sun_solaris=x86_64-sun-solaris2.10-g++ \ - CC_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-gcc-7 \ - CXX_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-g++-7 \ + CC_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-gcc-8 \ + CXX_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-g++-8 \ AR_x86_64_fortanix_unknown_sgx=ar \ CC_x86_64_fortanix_unknown_sgx=x86_64-fortanix-unknown-sgx-clang-11 \ CFLAGS_x86_64_fortanix_unknown_sgx="-mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ @@ -51,14 +52,14 @@ ENV \ AR_i686_unknown_freebsd=i686-unknown-freebsd11-ar \ CC_i686_unknown_freebsd=i686-unknown-freebsd11-clang \ CXX_i686_unknown_freebsd=i686-unknown-freebsd11-clang++ \ - CC=gcc-7 \ - CXX=g++-7 + CC=gcc-8 \ + CXX=g++-8 WORKDIR /build COPY scripts/musl.sh /build RUN env \ - CC=arm-linux-gnueabi-gcc-7 CFLAGS="-march=armv7-a" \ - CXX=arm-linux-gnueabi-g++-7 CXXFLAGS="-march=armv7-a" \ + CC=arm-linux-gnueabi-gcc-8 CFLAGS="-march=armv7-a" \ + CXX=arm-linux-gnueabi-g++-8 CXXFLAGS="-march=armv7-a" \ bash musl.sh armv7 && \ rm -rf /build/* @@ -108,7 +109,7 @@ ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabi ENV TARGETS=$TARGETS,i686-unknown-freebsd # As per https://bugs.launchpad.net/ubuntu/+source/gcc-defaults/+bug/1300211 -# we need asm in the search path for gcc-7 (for gnux32) but not in the search path of the +# we need asm in the search path for gcc-8 (for gnux32) but not in the search path of the # cross compilers. # Luckily one of the folders is /usr/local/include so symlink /usr/include/asm-generic there RUN ln -s /usr/include/asm-generic /usr/local/include/asm diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh index 6c4aa87526..14fb399aff 100755 --- a/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-various-2/build-solaris-toolchain.sh @@ -7,7 +7,7 @@ ARCH=$1 LIB_ARCH=$2 APT_ARCH=$3 BINUTILS=2.28.1 -GCC=6.4.0 +GCC=6.5.0 # First up, build binutils mkdir binutils diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh index c6db200f86..82d0f7dc47 100755 --- a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh @@ -1,18 +1,16 @@ #!/bin/sh -# -# ignore-tidy-linelength set -ex -# Originally from https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -curl https://ci-mirrors.rust-lang.org/rustc/clang%2Bllvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz | \ +# Originally from https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/clang+llvm-11.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz +curl https://ci-mirrors.rust-lang.org/rustc/2021-01-14-clang%2Bllvm-11.0.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz | \ tar xJf - -export PATH=`pwd`/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04/bin:$PATH +export PATH=`pwd`/clang+llvm-11.0.1-x86_64-linux-gnu-ubuntu-16.04/bin:$PATH git clone https://github.com/WebAssembly/wasi-libc cd wasi-libc -git reset --hard 215adc8ac9f91eb055311acc72683fd2eb1ae15a +git reset --hard 58795582905e08fa7748846c1971b4ab911d1e16 make -j$(nproc) INSTALL_DIR=/wasm32-wasi install cd .. diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh index 1c0ef6c2b3..f7acedcfb4 100755 --- a/src/ci/docker/host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh @@ -7,7 +7,7 @@ target="x86_64-fortanix-unknown-sgx" install_prereq() { curl https://apt.llvm.org/llvm-snapshot.gpg.key|apt-key add - - add-apt-repository -y 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' + add-apt-repository -y 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-11 main' apt-get update apt-get install -y --no-install-recommends \ build-essential \ diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 8653aecc12..f5fe546cdc 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -1,6 +1,6 @@ -FROM ubuntu:18.04 +FROM ubuntu:20.04 -RUN apt-get update && apt-get install -y --no-install-recommends \ +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ g++ \ make \ ninja-build \ @@ -44,7 +44,6 @@ ENV WASM_TARGETS=wasm32-unknown-unknown ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_TARGETS \ src/test/run-make \ src/test/ui \ - src/test/compile-fail \ src/test/mir-opt \ src/test/codegen-units \ library/core diff --git a/src/ci/docker/scripts/cross-apt-packages.sh b/src/ci/docker/scripts/cross-apt-packages.sh index 57cb6d5cda..2f8bf11942 100644 --- a/src/ci/docker/scripts/cross-apt-packages.sh +++ b/src/ci/docker/scripts/cross-apt-packages.sh @@ -1,5 +1,5 @@ #!/bin/sh -apt-get update && apt-get install -y --no-install-recommends \ +apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ automake \ bison \ bzip2 \ diff --git a/src/ci/pgo.sh b/src/ci/pgo.sh index 10f2cd825f..eb9354b911 100755 --- a/src/ci/pgo.sh +++ b/src/ci/pgo.sh @@ -26,6 +26,20 @@ pgo_perf_benchmark ctfe-stress-4 cp -pri ../src/tools/cargo /tmp/cargo +# The Cargo repository does not have a Cargo.lock in it, as it relies on the +# lockfile already present in the rust-lang/rust monorepo. This decision breaks +# down when Cargo is built outside the monorepo though (like in this case), +# resulting in a build without any dependency locking. +# +# To ensure Cargo is built with locked dependencies even during PGO profiling +# the following command copies the monorepo's lockfile into the Cargo temporary +# directory. Cargo will *not* keep that lockfile intact, as it will remove all +# the dependencies Cargo itself doesn't rely on. Still, it will prevent +# building Cargo with arbitrary dependency versions. +# +# See #81378 for the bug that prompted adding this. +cp -p ../Cargo.lock /tmp/cargo + # Build cargo (with some flags) function pgo_cargo { RUSTC=./build/$PGO_HOST/stage2/bin/rustc \ diff --git a/src/ci/run.sh b/src/ci/run.sh index 3a22496bcc..a408fa83e5 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -53,6 +53,11 @@ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-locked-deps" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-cargo-native-static" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.codegen-units-std=1" +# Only produce xz tarballs on CI. gz tarballs will be generated by the release +# process by recompressing the existing xz ones. This decreases the storage +# space required for CI artifacts. +RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --dist-compression-formats=xz" + if [ "$DIST_SRC" = "" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-dist-src" fi diff --git a/src/ci/scripts/install-awscli.sh b/src/ci/scripts/install-awscli.sh index f9b759fe34..3d8f0de7a3 100755 --- a/src/ci/scripts/install-awscli.sh +++ b/src/ci/scripts/install-awscli.sh @@ -27,7 +27,7 @@ if isLinux; then pip="pip3" pipflags="--user" - sudo apt-get install -y python3-setuptools + sudo apt-get install -y python3-setuptools python3-wheel ciCommandAddPath "${HOME}/.local/bin" fi diff --git a/src/doc/book/.github/workflows/main.yml b/src/doc/book/.github/workflows/main.yml index 83322a669b..976890d727 100644 --- a/src/doc/book/.github/workflows/main.yml +++ b/src/doc/book/.github/workflows/main.yml @@ -12,12 +12,12 @@ jobs: - name: Install Rust run: | rustup set profile minimal - rustup toolchain install 1.48 -c rust-docs - rustup default 1.48 + rustup toolchain install 1.49 -c rust-docs + rustup default 1.49 - name: Install mdbook run: | mkdir bin - curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.3.7/mdbook-v0.3.7-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin + curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.5/mdbook-v0.4.5-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin echo "$(pwd)/bin" >> ${GITHUB_PATH} - name: Report versions run: | @@ -41,7 +41,7 @@ jobs: - name: Install mdbook run: | mkdir bin - curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.3.7/mdbook-v0.3.7-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin + curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.5/mdbook-v0.4.5-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin echo "$(pwd)/bin" >> ${GITHUB_PATH} - name: Report versions run: | diff --git a/src/doc/book/ADMIN_TASKS.md b/src/doc/book/ADMIN_TASKS.md index 701375574b..036b0d8068 100644 --- a/src/doc/book/ADMIN_TASKS.md +++ b/src/doc/book/ADMIN_TASKS.md @@ -1,88 +1,103 @@ # Administrative Tasks -This documentation is for Carol and Steve and anyone else managing the repo to remember how to do -occasional maintenance tasks. +This documentation is for Carol and Steve and anyone else managing the repo to +remember how to do occasional maintenance tasks. ## Update the `rustc` version - Change the version number in `.github/workflows/main.yml` -- Change the version number in `rust-toolchain`, which should change the version you're using - locally with `rustup` +- Change the version number in `rust-toolchain`, which should change the + version you're using locally with `rustup` - Change the version number in `src/title-page.md` -- Run `./tools/update-rustc.sh` (see its commented code for details on what it does) -- Inspect the changes (by looking at the files changed according to git) and their effects (by - looking at the files in `tmp/book-before` and `tmp/book-after`) and commit them if they look good -- Grep for `manual-regeneration` and follow the instructions in those places to update output that - cannot be generated by a script +- Run `./tools/update-rustc.sh` (see its commented code for details on what it + does) +- Inspect the changes (by looking at the files changed according to git) and + their effects (by looking at the files in `tmp/book-before` and + `tmp/book-after`) and commit them if they look good +- Grep for `manual-regeneration` and follow the instructions in those places to + update output that cannot be generated by a script ## Release a new version of the listings -We now make `.tar` files of complete projects containing every listing available [as GitHub -Releases](https://github.com/rust-lang/book/releases). To create a new release artifact, for -example if there have been code changes due to edits or due to updating Rust and `rustfmt`, do the -following: - -- Create a git tag for the release and push it to GitHub, or create a new tag by going to the - GitHub UI, [drafting a new release](https://github.com/rust-lang/book/releases/new), and entering - a new tag instead of selecting an existing tag -- Run `cargo run --bin release_listings`, which will generate `tmp/listings.tar.gz` +We now make `.tar` files of complete projects containing every listing +available [as GitHub Releases](https://github.com/rust-lang/book/releases). To +create a new release artifact, for example if there have been code changes due +to edits or due to updating Rust and `rustfmt`, do the following: + +- Create a git tag for the release and push it to GitHub, or create a new tag + by going to the GitHub UI, [drafting a new + release](https://github.com/rust-lang/book/releases/new), and entering a new + tag instead of selecting an existing tag +- Run `cargo run --bin release_listings`, which will generate + `tmp/listings.tar.gz` - Upload `tmp/listings.tar.gz` in the GitHub UI for the draft release - Publish the release ## Add a new listing -To facilitate the scripts that run `rustfmt` on all the listings, update the output when the -compiler is updated, and produce release artifacts containing full projects for the listings, any -listing beyond the most trivial should be extracted into a file. To do that: +To facilitate the scripts that run `rustfmt` on all the listings, update the +output when the compiler is updated, and produce release artifacts containing +full projects for the listings, any listing beyond the most trivial should be +extracted into a file. To do that: - Find where the new listing should go in the `listings` directory. - There is one subdirectory for each chapter - - Numbered listings should use `listing-[chapter num]-[listing num]` for their directory names. - - Listings without a number should start with `no-listing-` followed by a number that indicates - its position in the chapter relative to the other listings without numbers in the chapter, then - a short description that someone could read to find the code they're looking for. - - Listings used only for displaying the output of the code (for example, when we say "if we had - written x instead of y, we would get this compiler error:" but we don't actually show code x) - should be named with `output-only-` followed by a number that indicates its position in the - chapter relative to the other listings used only for output, then a short description that - authors or contributors could read to find the code they're looking for. + - Numbered listings should use `listing-[chapter num]-[listing num]` for + their directory names. + - Listings without a number should start with `no-listing-` followed by a + number that indicates its position in the chapter relative to the other + listings without numbers in the chapter, then a short description that + someone could read to find the code they're looking for. + - Listings used only for displaying the output of the code (for example, when + we say "if we had written x instead of y, we would get this compiler + error:" but we don't actually show code x) should be named with + `output-only-` followed by a number that indicates its position in the + chapter relative to the other listings used only for output, then a short + description that authors or contributors could read to find the code + they're looking for. - **Remember to adjust surrounding listing numbers as appropriate!** -- Create a full Cargo project in that directory, either by using `cargo new` or copying another - listing as a starting point. +- Create a full Cargo project in that directory, either by using `cargo new` or + copying another listing as a starting point. - Add the code and any surrounding code needed to create a full working example. -- If you only want to show part of the code in the file, use anchor comments (`// ANCHOR: some_tag` - and `// ANCHOR_END: some_tag`) to mark the parts of the file you want to show. -- For Rust code, use the `{{#rustdoc_include [fileame:some_tag]}}` directive within the code blocks - in the text. The `rustdoc_include` directive gives the code that doesn't get displayed to - `rustdoc` for `mdbook test` purposes. +- If you only want to show part of the code in the file, use anchor comments + (`// ANCHOR: some_tag` and `// ANCHOR_END: some_tag`) to mark the parts of + the file you want to show. +- For Rust code, use the `{{#rustdoc_include [fileame:some_tag]}}` directive + within the code blocks in the text. The `rustdoc_include` directive gives the + code that doesn't get displayed to `rustdoc` for `mdbook test` purposes. - For anything else, use the `{{#include [filename:some_tag]}}` directive. -- If you want to display the output of a command in the text as well, create an `output.txt` file - in the listing's directory as follows: - - Run the command, like `cargo run` or `cargo test`, and copy all of the output. - - Create a new `output.txt` file with the first line `$ [the command you ran]`. +- If you want to display the output of a command in the text as well, create an + `output.txt` file in the listing's directory as follows: + - Run the command, like `cargo run` or `cargo test`, and copy all of the + output. + - Create a new `output.txt` file with the first line `$ [the command you + ran]`. - Paste the output you just copied. - - Run `./tools/update-rustc.sh`, which should perform some normalization on the compiler output. + - Run `./tools/update-rustc.sh`, which should perform some normalization on + the compiler output. - Include the output in the text with the `{{#include [filename]}}` directive. - Add and commit output.txt. -- If you want to display output but for some reason it can't be generated by a script (say, because - of user input or external events like making a web request), keep the output inline but make a - comment that contains `manual-regeneration` and instructions for manually updating the inline - output. -- If you don't want this example to even be attempted to be formatted by `rustfmt` (for example - because the example doesn't parse on purpose), add a `rustfmt-ignore` file in the listing's - directory and the reason it's not being formatted as the contents of that file (in case it's a - rustfmt bug that might get fixed someday). +- If you want to display output but for some reason it can't be generated by a + script (say, because of user input or external events like making a web + request), keep the output inline but make a comment that contains + `manual-regeneration` and instructions for manually updating the inline + output. +- If you don't want this example to even be attempted to be formatted by + `rustfmt` (for example because the example doesn't parse on purpose), add a + `rustfmt-ignore` file in the listing's directory and the reason it's not + being formatted as the contents of that file (in case it's a rustfmt bug that + might get fixed someday). ## See the effect of some change on the rendered book To check, say, updating `mdbook` or changing the way files get included: -- Generate a built book before the change you want to test by running `mdbook build -d - tmp/book-before` +- Generate a built book before the change you want to test by running `mdbook + build -d tmp/book-before` - Apply the changes you want to test and run `mdbook build -d tmp/book-after` - Run `./tools/megadiff.sh` -- Files remaining in `tmp/book-before` and `tmp/book-after` have differences you can manually - inspect with your favorite diff viewing mechanism +- Files remaining in `tmp/book-before` and `tmp/book-after` have differences + you can manually inspect with your favorite diff viewing mechanism ## Produce new markdown files for No Starch diff --git a/src/doc/book/listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/output.txt b/src/doc/book/listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/output.txt deleted file mode 100644 index a4dd90c0bd..0000000000 --- a/src/doc/book/listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/output.txt +++ /dev/null @@ -1,15 +0,0 @@ -$ cargo run - Compiling arrays v0.1.0 (file:///projects/arrays) -error: this operation will panic at runtime - --> src/main.rs:5:19 - | -5 | let element = a[index]; - | ^^^^^^^^ index out of bounds: the length is 5 but the index is 10 - | - = note: `#[deny(unconditional_panic)]` on by default - -error: aborting due to previous error - -error: could not compile `arrays` - -To learn more, run the command again with --verbose. diff --git a/src/doc/book/listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/src/main.rs b/src/doc/book/listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/src/main.rs index 5ce0ed4a5b..5e5121684d 100644 --- a/src/doc/book/listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/src/main.rs +++ b/src/doc/book/listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/src/main.rs @@ -1,8 +1,25 @@ +use std::io; + fn main() { let a = [1, 2, 3, 4, 5]; - let index = 10; + + println!("Please enter an array index."); + + let mut index = String::new(); + + io::stdin() + .read_line(&mut index) + .expect("Failed to read line"); + + let index: usize = index + .trim() + .parse() + .expect("Index entered was not a number"); let element = a[index]; - println!("The value of element is: {}", element); + println!( + "The value of the element at index {} is: {}", + index, element + ); } diff --git a/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-12-if-let/src/main.rs b/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-12-if-let/src/main.rs index fb6578cf64..a2bc0e3dca 100644 --- a/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-12-if-let/src/main.rs +++ b/src/doc/book/listings/ch06-enums-and-pattern-matching/no-listing-12-if-let/src/main.rs @@ -1,6 +1,6 @@ fn main() { - let some_u8_value = Some(0u8); // ANCHOR: here + let some_u8_value = Some(0u8); if let Some(3) = some_u8_value { println!("three"); } diff --git a/src/doc/book/listings/ch11-writing-automated-tests/listing-11-10/output.txt b/src/doc/book/listings/ch11-writing-automated-tests/listing-11-10/output.txt index 817543cd2b..a0484d2605 100644 --- a/src/doc/book/listings/ch11-writing-automated-tests/listing-11-10/output.txt +++ b/src/doc/book/listings/ch11-writing-automated-tests/listing-11-10/output.txt @@ -13,7 +13,7 @@ failures: I got the value 8 thread 'main' panicked at 'assertion failed: `(left == right)` left: `5`, - right: `10`', src/lib.rs:20:9 + right: `10`', src/lib.rs:19:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-03/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-03/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-03/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-03/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-04/output.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-04/output.txt index 75e7863ef5..5f31c8c292 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-04/output.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-04/output.txt @@ -5,10 +5,10 @@ $ cargo run the poem.txt Searching for the In file poem.txt With text: -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-04/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-04/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-04/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-04/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-05/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-05/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-05/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-05/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-06/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-06/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-06/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-06/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-07/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-07/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-07/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-07/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-08/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-08/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-08/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-08/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-09/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-09/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-09/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-09/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-10/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-10/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-10/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-10/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-11/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-11/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-11/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-11/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-12/output.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-12/output.txt index 71562ed41c..8d1c221872 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-12/output.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-12/output.txt @@ -16,10 +16,10 @@ warning: 1 warning emitted Searching for the In file poem.txt With text: -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-12/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-12/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-12/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-12/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-13/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-13/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-13/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-13/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-14/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-14/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-14/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-14/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-15/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-15/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-15/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-15/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-16/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-16/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-16/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-16/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-17/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-17/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-17/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-17/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-18/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-18/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-18/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-18/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-19/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-19/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-19/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-19/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-20/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-20/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-20/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-20/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-21/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-21/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-21/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-21/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-22/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-22/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-22/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-22/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-23/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-23/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-23/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-23/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/listing-12-24/poem.txt b/src/doc/book/listings/ch12-an-io-project/listing-12-24/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/listing-12-24/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/listing-12-24/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/no-listing-01-handling-errors-in-main/poem.txt b/src/doc/book/listings/ch12-an-io-project/no-listing-01-handling-errors-in-main/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/no-listing-01-handling-errors-in-main/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/no-listing-01-handling-errors-in-main/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/no-listing-02-using-search-in-run/poem.txt b/src/doc/book/listings/ch12-an-io-project/no-listing-02-using-search-in-run/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/no-listing-02-using-search-in-run/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/no-listing-02-using-search-in-run/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/output-only-02-missing-lifetimes/poem.txt b/src/doc/book/listings/ch12-an-io-project/output-only-02-missing-lifetimes/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/output-only-02-missing-lifetimes/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/output-only-02-missing-lifetimes/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/output-only-03-multiple-matches/output.txt b/src/doc/book/listings/ch12-an-io-project/output-only-03-multiple-matches/output.txt index c0ab7efd97..2b19bf956e 100644 --- a/src/doc/book/listings/ch12-an-io-project/output-only-03-multiple-matches/output.txt +++ b/src/doc/book/listings/ch12-an-io-project/output-only-03-multiple-matches/output.txt @@ -2,6 +2,6 @@ $ cargo run body poem.txt Compiling minigrep v0.1.0 (file:///projects/minigrep) Finished dev [unoptimized + debuginfo] target(s) in 0.0s Running `target/debug/minigrep body poem.txt` -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? How dreary to be somebody! diff --git a/src/doc/book/listings/ch12-an-io-project/output-only-03-multiple-matches/poem.txt b/src/doc/book/listings/ch12-an-io-project/output-only-03-multiple-matches/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/output-only-03-multiple-matches/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/output-only-03-multiple-matches/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch12-an-io-project/output-only-04-no-matches/poem.txt b/src/doc/book/listings/ch12-an-io-project/output-only-04-no-matches/poem.txt index 208e35a5ec..8707527313 100644 --- a/src/doc/book/listings/ch12-an-io-project/output-only-04-no-matches/poem.txt +++ b/src/doc/book/listings/ch12-an-io-project/output-only-04-no-matches/poem.txt @@ -1,7 +1,7 @@ -I’m nobody! Who are you? +I'm nobody! Who are you? Are you nobody, too? -Then there’s a pair of us - don’t tell! -They’d banish us, you know. +Then there's a pair of us - don't tell! +They'd banish us, you know. How dreary to be somebody! How public, like a frog diff --git a/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/output.txt b/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/output.txt index 8c67a059d9..40d6c7f9fa 100644 --- a/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/output.txt +++ b/src/doc/book/listings/ch17-oop/no-listing-01-trait-object-of-clone/output.txt @@ -4,9 +4,10 @@ error[E0038]: the trait `Clone` cannot be made into an object --> src/lib.rs:2:21 | 2 | pub components: Vec>, - | ^^^^^^^^^^^^^^^^^^^ the trait `Clone` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^ `Clone` cannot be made into an object | = note: the trait cannot be made into an object because it requires `Self: Sized` + = note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit error: aborting due to previous error diff --git a/src/doc/book/rust-toolchain b/src/doc/book/rust-toolchain index c05d9129f1..d4d0f85c9e 100644 --- a/src/doc/book/rust-toolchain +++ b/src/doc/book/rust-toolchain @@ -1 +1 @@ -1.48 +1.49 diff --git a/src/doc/book/src/ch03-02-data-types.md b/src/doc/book/src/ch03-02-data-types.md index 85063f71cd..1b93f7892e 100644 --- a/src/doc/book/src/ch03-02-data-types.md +++ b/src/doc/book/src/ch03-02-data-types.md @@ -324,8 +324,8 @@ get the value `2` from index `[1]` in the array. ##### Invalid Array Element Access What happens if you try to access an element of an array that is past the end -of the array? Say you change the example to the following code, which will -compile but exit with an error when it runs: +of the array? Say you change the example to the following, which uses code +similar to the guessing game in Chapter 2 to get an array index from the user: Filename: src/main.rs @@ -333,17 +333,30 @@ compile but exit with an error when it runs: {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/src/main.rs}} ``` -Running this code using `cargo run` produces the following result: +This code compiles successfully. If you run this code using `cargo run` and +enter 0, 1, 2, 3, or 4, the program will print out the corresponding value at +that index in the array. If you instead enter a number past the end of the +array, such as 10, you'll see output like this: + + ```console -{{#include ../listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/output.txt}} +thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ``` -The compilation didn’t produce any errors, but the program resulted in a -*runtime* error and didn’t exit successfully. When you attempt to access an +The program resulted in a *runtime* error at the point of using an invalid +value in the indexing operation. The program exited at that point with an error +message and didn't execute the final `println!`. When you attempt to access an element using indexing, Rust will check that the index you’ve specified is less than the array length. If the index is greater than or equal to the array -length, Rust will panic. +length, Rust will panic. This check has to happen at runtime, especially in +this case, because the compiler can't possibly know what the value a user +running the code will later enter. This is the first example of Rust’s safety principles in action. In many low-level languages, this kind of check is not done, and when you provide an diff --git a/src/doc/book/src/ch04-01-what-is-ownership.md b/src/doc/book/src/ch04-01-what-is-ownership.md index 0a8c451d77..fad2f7e3f8 100644 --- a/src/doc/book/src/ch04-01-what-is-ownership.md +++ b/src/doc/book/src/ch04-01-what-is-ownership.md @@ -215,8 +215,8 @@ from Listing 4-1 using a `String` instead of a string literal: ``` There is a natural point at which we can return the memory our `String` needs -to the allocator: when `s` goes out of scope. When a variable goes out -of scope, Rust calls a special function for us. This function is called `drop`, +to the allocator: when `s` goes out of scope. When a variable goes out of +scope, Rust calls a special function for us. This function is called [`drop`], and it’s where the author of `String` can put the code to return the memory. Rust calls `drop` automatically at the closing curly bracket. @@ -466,3 +466,4 @@ common. Luckily for us, Rust has a feature for this concept, called [derivable-traits]: appendix-03-derivable-traits.html [method-syntax]: ch05-03-method-syntax.html#method-syntax [paths-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html +[`drop`]: ../std/ops/trait.Drop.html#tymethod.drop diff --git a/src/doc/book/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md b/src/doc/book/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md index 20bb9b43df..fc3980e468 100644 --- a/src/doc/book/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md +++ b/src/doc/book/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md @@ -215,7 +215,7 @@ or even see which fruit they’ll get. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-09/src/lib.rs}} ``` @@ -240,7 +240,7 @@ only need the `pub` before the `enum` keyword, as shown in Listing 7-10. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-10/src/lib.rs}} ``` diff --git a/src/doc/book/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md b/src/doc/book/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md index 01f2dfcaf1..3e4f1aa69b 100644 --- a/src/doc/book/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md +++ b/src/doc/book/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md @@ -88,7 +88,7 @@ different parent modules and how to refer to them. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-15/src/lib.rs:here}} ``` @@ -109,7 +109,7 @@ code in Listing 7-15 by renaming one of the two `Result` types using `as`. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-16/src/lib.rs:here}} ``` @@ -244,7 +244,7 @@ two `use` statements that share a subpath. For example, Listing 7-19 shows two Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-19/src/lib.rs}} ``` @@ -257,7 +257,7 @@ the nested path, as shown in Listing 7-20. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-20/src/lib.rs}} ``` diff --git a/src/doc/book/src/ch10-02-traits.md b/src/doc/book/src/ch10-02-traits.md index 79f941581c..55f22cfb13 100644 --- a/src/doc/book/src/ch10-02-traits.md +++ b/src/doc/book/src/ch10-02-traits.md @@ -29,7 +29,7 @@ need a summary from each type, and we need to request that summary by calling a Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-12/src/lib.rs}} ``` @@ -62,7 +62,7 @@ already limited to 280 characters. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-13/src/lib.rs:here}} ``` @@ -130,7 +130,7 @@ in Listing 10-12. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-14/src/lib.rs:here}} ``` @@ -166,7 +166,7 @@ a small part of it. For example, we could define the `Summary` trait to have a `summarize` method that has a default implementation that calls the `summarize_author` method: -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-03-default-impl-calls-other-methods/src/lib.rs:here}} ``` @@ -416,7 +416,7 @@ the `Display` trait that enables printing. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-16/src/lib.rs}} ``` diff --git a/src/doc/book/src/ch11-02-running-tests.md b/src/doc/book/src/ch11-02-running-tests.md index 880d1b38b2..c33280e349 100644 --- a/src/doc/book/src/ch11-02-running-tests.md +++ b/src/doc/book/src/ch11-02-running-tests.md @@ -105,7 +105,7 @@ To demonstrate how to run a subset of tests, we’ll create three tests for our Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-11/src/lib.rs}} ``` diff --git a/src/doc/book/src/ch11-03-test-organization.md b/src/doc/book/src/ch11-03-test-organization.md index a6adb65a28..0057e8c472 100644 --- a/src/doc/book/src/ch11-03-test-organization.md +++ b/src/doc/book/src/ch11-03-test-organization.md @@ -37,7 +37,7 @@ this chapter, Cargo generated this code for us: Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-01/src/lib.rs:here}} ``` diff --git a/src/doc/book/src/ch13-01-closures.md b/src/doc/book/src/ch13-01-closures.md index 48cc5bc6e1..27e56e02a1 100644 --- a/src/doc/book/src/ch13-01-closures.md +++ b/src/doc/book/src/ch13-01-closures.md @@ -278,7 +278,7 @@ The compiler gives us this error: The first time we call `example_closure` with the `String` value, the compiler infers the type of `x` and the return type of the closure to be `String`. Those -types are then locked in to the closure in `example_closure`, and we get a type +types are then locked into the closure in `example_closure`, and we get a type error if we try to use a different type with the same closure. ### Storing Closures Using Generic Parameters and the `Fn` Traits @@ -430,8 +430,8 @@ Run this test with the `Cacher` implementation in Listing 13-9 and Listing ``` The problem is that the first time we called `c.value` with 1, the `Cacher` -instance saved `Some(1)` in `self.value`. Thereafter, no matter what we pass in -to the `value` method, it will always return 1. +instance saved `Some(1)` in `self.value`. Thereafter, no matter what we pass into +the `value` method, it will always return 1. Try modifying `Cacher` to hold a hash map rather than a single value. The keys of the hash map will be the `arg` values that are passed in, and the values of diff --git a/src/doc/book/src/ch14-03-cargo-workspaces.md b/src/doc/book/src/ch14-03-cargo-workspaces.md index 3801742cd8..c376fa57b8 100644 --- a/src/doc/book/src/ch14-03-cargo-workspaces.md +++ b/src/doc/book/src/ch14-03-cargo-workspaces.md @@ -122,7 +122,7 @@ In the *add-one/src/lib.rs* file, let’s add an `add_one` function: Filename: add-one/src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add-one/src/lib.rs}} ``` @@ -285,7 +285,7 @@ within the `add_one` crate: Filename: add-one/src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add-one/src/lib.rs}} ``` diff --git a/src/doc/book/src/ch15-05-interior-mutability.md b/src/doc/book/src/ch15-05-interior-mutability.md index a1849025f5..4b1f414732 100644 --- a/src/doc/book/src/ch15-05-interior-mutability.md +++ b/src/doc/book/src/ch15-05-interior-mutability.md @@ -124,7 +124,7 @@ called `Messenger`. Listing 15-20 shows the library code: Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch15-smart-pointers/listing-15-20/src/lib.rs}} ``` diff --git a/src/doc/book/src/ch16-03-shared-state.md b/src/doc/book/src/ch16-03-shared-state.md index 2c934ddb8c..8704423606 100644 --- a/src/doc/book/src/ch16-03-shared-state.md +++ b/src/doc/book/src/ch16-03-shared-state.md @@ -134,9 +134,7 @@ multiple-ownership method we discussed in Chapter 15. In Chapter 15, we gave a value multiple owners by using the smart pointer `Rc` to create a reference counted value. Let’s do the same here and see what happens. We’ll wrap the `Mutex` in `Rc` in Listing 16-14 and clone -the `Rc` before moving ownership to the thread. Now that we’ve seen the -errors, we’ll also switch back to using the `for` loop, and we’ll keep the -`move` keyword with the closure. +the `Rc` before moving ownership to the thread. Filename: src/main.rs diff --git a/src/doc/book/src/ch17-01-what-is-oo.md b/src/doc/book/src/ch17-01-what-is-oo.md index e197cbfa84..86bf467a8c 100644 --- a/src/doc/book/src/ch17-01-what-is-oo.md +++ b/src/doc/book/src/ch17-01-what-is-oo.md @@ -46,7 +46,7 @@ cache the calculated average for us. Listing 17-1 has the definition of the Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-01/src/lib.rs}} ``` @@ -62,7 +62,7 @@ on the struct, as shown in Listing 17-2: Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-02/src/lib.rs:here}} ``` diff --git a/src/doc/book/src/ch17-02-trait-objects.md b/src/doc/book/src/ch17-02-trait-objects.md index 52a3371bd0..90535ddfa7 100644 --- a/src/doc/book/src/ch17-02-trait-objects.md +++ b/src/doc/book/src/ch17-02-trait-objects.md @@ -68,7 +68,7 @@ Listing 17-3 shows how to define a trait named `Draw` with one method named Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-03/src/lib.rs}} ``` @@ -82,7 +82,7 @@ a `Box` that implements the `Draw` trait. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-04/src/lib.rs:here}} ``` @@ -95,7 +95,7 @@ On the `Screen` struct, we’ll define a method named `run` that will call the Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-05/src/lib.rs:here}} ``` @@ -111,7 +111,7 @@ as in Listing 17-6: Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-06/src/lib.rs:here}} ``` diff --git a/src/doc/book/src/ch17-03-oo-design-patterns.md b/src/doc/book/src/ch17-03-oo-design-patterns.md index 876f9c02b2..4944e96079 100644 --- a/src/doc/book/src/ch17-03-oo-design-patterns.md +++ b/src/doc/book/src/ch17-03-oo-design-patterns.md @@ -75,7 +75,7 @@ inside an `Option` in a private field named `state`. You’ll see why the Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-12/src/lib.rs}} ``` @@ -209,7 +209,7 @@ state is approved, as shown in Listing 17-16: Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-16/src/lib.rs:here}} ``` @@ -267,7 +267,7 @@ Listing 17-18: Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-18/src/lib.rs:here}} ``` @@ -375,7 +375,7 @@ as well as methods on each: Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-19/src/lib.rs}} ``` @@ -411,7 +411,7 @@ shown in Listing 17-20: Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch17-oop/listing-17-20/src/lib.rs:here}} ``` diff --git a/src/doc/book/src/ch19-03-advanced-traits.md b/src/doc/book/src/ch19-03-advanced-traits.md index d527bb67a5..1d396463ec 100644 --- a/src/doc/book/src/ch19-03-advanced-traits.md +++ b/src/doc/book/src/ch19-03-advanced-traits.md @@ -27,7 +27,7 @@ Method”][the-iterator-trait-and-the-next-method] section of Chapter 13, we mentioned that the definition of the `Iterator` trait is as shown in Listing 19-12. -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch19-advanced-features/listing-19-12/src/lib.rs}} ``` @@ -56,7 +56,7 @@ Listing 13-21, we specified that the `Item` type was `u32`: This syntax seems comparable to that of generics. So why not just define the `Iterator` trait with generics, as shown in Listing 19-13? -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch19-advanced-features/listing-19-13/src/lib.rs}} ``` @@ -143,7 +143,7 @@ implementation of `Add` do the conversion correctly. We can implement `Add` for Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch19-advanced-features/listing-19-15/src/lib.rs}} ``` diff --git a/src/doc/book/src/ch19-04-advanced-types.md b/src/doc/book/src/ch19-04-advanced-types.md index b10b4cbb41..c30a847372 100644 --- a/src/doc/book/src/ch19-04-advanced-types.md +++ b/src/doc/book/src/ch19-04-advanced-types.md @@ -101,7 +101,7 @@ possible I/O errors. Many of the functions in `std::io` will be returning `Result` where the `E` is `std::io::Error`, such as these functions in the `Write` trait: -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch19-advanced-features/no-listing-05-write-trait/src/lib.rs}} ``` diff --git a/src/doc/book/src/ch19-05-advanced-functions-and-closures.md b/src/doc/book/src/ch19-05-advanced-functions-and-closures.md index 876c18e0c3..51866ab2ea 100644 --- a/src/doc/book/src/ch19-05-advanced-functions-and-closures.md +++ b/src/doc/book/src/ch19-05-advanced-functions-and-closures.md @@ -105,7 +105,7 @@ The error references the `Sized` trait again! Rust doesn’t know how much space it will need to store the closure. We saw a solution to this problem earlier. We can use a trait object: -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch19-advanced-features/no-listing-19-returns-closure-trait-object/src/lib.rs}} ``` diff --git a/src/doc/book/src/ch19-06-macros.md b/src/doc/book/src/ch19-06-macros.md index 3d1af7444a..a714413b6f 100644 --- a/src/doc/book/src/ch19-06-macros.md +++ b/src/doc/book/src/ch19-06-macros.md @@ -77,7 +77,7 @@ Listing 19-28 shows a slightly simplified definition of the `vec!` macro. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch19-advanced-features/listing-19-28/src/lib.rs}} ``` @@ -230,7 +230,7 @@ Next, we’ll define the `HelloMacro` trait and its associated function: Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch19-advanced-features/no-listing-20-impl-hellomacro-for-pancakes/hello_macro/src/lib.rs}} ``` diff --git a/src/doc/book/src/ch20-02-multithreaded.md b/src/doc/book/src/ch20-02-multithreaded.md index 7c9e758a5a..24c7206979 100644 --- a/src/doc/book/src/ch20-02-multithreaded.md +++ b/src/doc/book/src/ch20-02-multithreaded.md @@ -157,7 +157,7 @@ definition of a `ThreadPool` struct that we can have for now: Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch20-web-server/no-listing-01-define-threadpool-struct/src/lib.rs}} ``` @@ -559,7 +559,7 @@ shown in Listing 20-20 to `Worker::new`. Filename: src/lib.rs -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch20-web-server/listing-20-20/src/lib.rs:here}} ``` diff --git a/src/doc/book/src/title-page.md b/src/doc/book/src/title-page.md index 2dfe5a102f..96e16ad4d0 100644 --- a/src/doc/book/src/title-page.md +++ b/src/doc/book/src/title-page.md @@ -2,7 +2,7 @@ *by Steve Klabnik and Carol Nichols, with contributions from the Rust Community* -This version of the text assumes you’re using Rust 1.48 or later with +This version of the text assumes you’re using Rust 1.49 or later with `edition="2018"` in *Cargo.toml* of all projects to use Rust 2018 Edition idioms. See the [“Installation” section of Chapter 1][install] to install or update Rust, and see the new [Appendix E][editions] ## Main Functions @@ -168,11 +149,6 @@ or `-` (U+002D) characters. [_shebang_]: https://en.wikipedia.org/wiki/Shebang_(Unix) [_utf8 byte order mark_]: https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8 [`Termination`]: ../std/process/trait.Termination.html -[`core`]: ../core/index.html -[`core::prelude::v1`]: ../core/prelude/index.html -[`extern crate`]: items/extern-crates.md -[`std`]: ../std/index.html -[`std::prelude::v1`]: ../std/prelude/index.html [attribute]: attributes.md [attributes]: attributes.md [comments]: comments.md @@ -182,3 +158,17 @@ or `-` (U+002D) characters. [trait or lifetime bounds]: trait-bounds.md [where clauses]: items/generics.md#where-clauses [whitespace]: whitespace.md + + diff --git a/src/doc/reference/src/expressions.md b/src/doc/reference/src/expressions.md index 9cc684b99a..c2702a342a 100644 --- a/src/doc/reference/src/expressions.md +++ b/src/doc/reference/src/expressions.md @@ -45,13 +45,12 @@ An expression may have two roles: it always produces a *value*, and it may have *effects* (otherwise known as "side effects"). An expression *evaluates to* a value, and has effects during *evaluation*. Many expressions contain -sub-expressions (operands). The meaning of each kind of expression dictates -several things: +sub-expressions, called the *operands* of the expression. The meaning of each +kind of expression dictates several things: -* Whether or not to evaluate the sub-expressions when evaluating the expression -* The order in which to evaluate the sub-expressions -* How to combine the sub-expressions' values to obtain the value of the - expression +* Whether or not to evaluate the operands when evaluating the expression +* The order in which to evaluate the operands +* How to combine the operands' values to obtain the value of the expression In this way, the structure of expressions dictates the structure of execution. Blocks are just another kind of expression, so blocks, statements, expressions, @@ -85,6 +84,58 @@ in the order given by their associativity. | `=` `+=` `-=` `*=` `/=` `%=`
`&=` |= `^=` `<<=` `>>=` | right to left | | `return` `break` closures | | +## Evaluation order of operands + +The following list of expressions all evaluate their operands the same way, as +described after the list. Other expressions either don't take operands or +evaluate them conditionally as described on their respective pages. + +* Dereference expression +* Error propagation expression +* Negation expression +* Arithmetic and logical binary operators +* Comparison operators +* Type cast expression +* Grouped expression +* Array expression +* Await expression +* Index expression +* Tuple expression +* Tuple index expression +* Struct expression +* Enumeration variant expression +* Call expression +* Method call expression +* Field expression +* Break expression +* Range expression +* Return expression + +The operands of these expressions are evaluated prior to applying the effects of +the expression. Expressions taking multiple operands are evaluated left to right +as written in the source code. + +> **Note**: Which subexpressions are the operands of an expression is +> determined by expression precedence as per the previous section. + +For example, the two `next` method calls will always be called in the same +order: + +```rust +# // Using vec instead of array to avoid references +# // since there is no stable owned array iterator +# // at the time this example was written. +let mut one_two = vec![1, 2].into_iter(); +assert_eq!( + (1, 2), + (one_two.next().unwrap(), one_two.next().unwrap()) +); +``` + +> **Note**: Since this is applied recursively, these expressions are also +> evaluated from innermost to outermost, ignoring siblings until there are no +> inner subexpressions. + ## Place Expressions and Value Expressions Expressions are divided into two main categories: place expressions and diff --git a/src/doc/reference/src/expressions/array-expr.md b/src/doc/reference/src/expressions/array-expr.md index 1b0699890d..0fd5427a9f 100644 --- a/src/doc/reference/src/expressions/array-expr.md +++ b/src/doc/reference/src/expressions/array-expr.md @@ -10,19 +10,30 @@ >       [_Expression_] ( `,` [_Expression_] )\* `,`?\ >    | [_Expression_] `;` [_Expression_] -An _[array](../types/array.md) expression_ can be written by -enclosing zero or more comma-separated expressions of uniform type in square -brackets. This produces an array containing each of these values in the -order they are written. +An _[array] expression_ can be written by enclosing zero or more +comma-separated expressions of uniform type in square brackets. This produces +an array containing each of these values in the order they are written. Alternatively there can be exactly two expressions inside the brackets, -separated by a semi-colon. The expression after the `;` must have type -`usize` and be a [constant expression], -such as a [literal](../tokens.md#literals) or a [constant -item](../items/constant-items.md). `[a; b]` creates an array containing `b` -copies of the value of `a`. If the expression after the semi-colon has a value -greater than 1 then this requires that the type of `a` is -[`Copy`](../special-types-and-traits.md#copy). +separated by a semicolon. The expression after the `;` must have type `usize` +and be a [constant expression], such as a [literal] or a [constant item]. `[a; +b]` creates an array containing `b` copies of the value of `a`. If the +expression after the semicolon has a value greater than 1 then this requires +that the type of `a` is [`Copy`], or `a` must be a path to a constant item. + +When the repeat expression `a` is a constant item, it is evaluated `b` times. +If `b` is 0, the constant item is not evaluated at all. For expressions that +are not a constant item, it is evaluated exactly once, and then the result is +copied `b` times. + +

+ +Warning: In the case where `b` is 0, and `a` is a non-constant item, there is +currently a bug in `rustc` where the value `a` is evaluated but not dropped, +thus causing a leak. See [issue +#74836](https://github.com/rust-lang/rust/issues/74836). + +
```rust [1, 2, 3, 4]; @@ -30,6 +41,8 @@ greater than 1 then this requires that the type of `a` is [0; 128]; // array with 128 zeros [0u8, 0u8, 0u8, 0u8,]; [[1, 0, 0], [0, 1, 0], [0, 0, 1]]; // 2D array +const EMPTY: Vec = Vec::new(); +[EMPTY; 2]; ``` ### Array expression attributes @@ -44,10 +57,9 @@ expressions]. > _IndexExpression_ :\ >    [_Expression_] `[` [_Expression_] `]` -[Array](../types/array.md) and [slice](../types/slice.md)-typed expressions can be -indexed by writing a square-bracket-enclosed expression of type `usize` (the -index) after them. When the array is mutable, the resulting [memory location] -can be assigned to. +[Array] and [slice]-typed expressions can be indexed by writing a +square-bracket-enclosed expression of type `usize` (the index) after them. +When the array is mutable, the resulting [memory location] can be assigned to. For other types an index expression `a[b]` is equivalent to `*std::ops::Index::index(&a, b)`, or @@ -81,11 +93,16 @@ arr[10]; // warning: index out of bounds The array index expression can be implemented for types other than arrays and slices by implementing the [Index] and [IndexMut] traits. +[`Copy`]: ../special-types-and-traits.md#copy [IndexMut]: ../../std/ops/trait.IndexMut.html [Index]: ../../std/ops/trait.Index.html [Inner attributes]: ../attributes.md [_Expression_]: ../expressions.md [_InnerAttribute_]: ../attributes.md +[array]: ../types/array.md [attributes on block expressions]: block-expr.md#attributes-on-block-expressions [constant expression]: ../const_eval.md#constant-expressions +[constant item]: ../items/constant-items.md +[literal]: ../tokens.md#literals [memory location]: ../expressions.md#place-expressions-and-value-expressions +[slice]: ../types/slice.md diff --git a/src/doc/reference/src/expressions/operator-expr.md b/src/doc/reference/src/expressions/operator-expr.md index 7afe96d2e6..acc6da26be 100644 --- a/src/doc/reference/src/expressions/operator-expr.md +++ b/src/doc/reference/src/expressions/operator-expr.md @@ -202,18 +202,18 @@ types. Remember that signed integers are always represented using two's complement. The operands of all of these operators are evaluated in [value expression context][value expression] so are moved or copied. -| Symbol | Integer | `bool` | Floating Point | Overloading Trait | -|--------|-------------------------|-------------|----------------|--------------------| -| `+` | Addition | | Addition | `std::ops::Add` | -| `-` | Subtraction | | Subtraction | `std::ops::Sub` | -| `*` | Multiplication | | Multiplication | `std::ops::Mul` | -| `/` | Division* | | Division | `std::ops::Div` | -| `%` | Remainder | | Remainder | `std::ops::Rem` | -| `&` | Bitwise AND | Logical AND | | `std::ops::BitAnd` | -| | | Bitwise OR | Logical OR | | `std::ops::BitOr` | -| `^` | Bitwise XOR | Logical XOR | | `std::ops::BitXor` | -| `<<` | Left Shift | | | `std::ops::Shl` | -| `>>` | Right Shift** | | | `std::ops::Shr` | +| Symbol | Integer | `bool` | Floating Point | Overloading Trait | Overloading Compound Assignment Trait | +|--------|-------------------------|-------------|----------------|--------------------| ------------------------------------- | +| `+` | Addition | | Addition | `std::ops::Add` | `std::ops::AddAssign` | +| `-` | Subtraction | | Subtraction | `std::ops::Sub` | `std::ops::SubAssign` | +| `*` | Multiplication | | Multiplication | `std::ops::Mul` | `std::ops::MulAssign` | +| `/` | Division* | | Division | `std::ops::Div` | `std::ops::DivAssign` | +| `%` | Remainder | | Remainder | `std::ops::Rem` | `std::ops::RemAssign` | +| `&` | Bitwise AND | Logical AND | | `std::ops::BitAnd` | `std::ops::BitAndAssign` | +| | | Bitwise OR | Logical OR | | `std::ops::BitOr` | `std::ops::BitOrAssign` | +| `^` | Bitwise XOR | Logical XOR | | `std::ops::BitXor` | `std::ops::BitXorAssign` | +| `<<` | Left Shift | | | `std::ops::Shl` | `std::ops::ShlAssign` | +| `>>` | Right Shift** | | | `std::ops::Shr` | `std::ops::ShrAssign` | \* Integer division rounds towards zero. @@ -246,19 +246,18 @@ assert_eq!(-10 >> 2, -3); >    | [_Expression_] `>=` [_Expression_]\ >    | [_Expression_] `<=` [_Expression_] -Comparison operators are also defined both for primitive types and many type in -the standard library. Parentheses are required when chaining comparison +Comparison operators are also defined both for primitive types and many types +in the standard library. Parentheses are required when chaining comparison operators. For example, the expression `a == b == c` is invalid and may be written as `(a == b) == c`. -Unlike arithmetic and logical operators, the traits for -overloading the operators the traits for these operators are used more -generally to show how a type may be compared and will likely be assumed to -define actual comparisons by functions that use these traits as bounds. Many -functions and macros in the standard library can then use that assumption -(although not to ensure safety). Unlike the arithmetic and logical operators -above, these operators implicitly take shared borrows of their operands, -evaluating them in [place expression context][place expression]: +Unlike arithmetic and logical operators, the traits for overloading these +operators are used more generally to show how a type may be compared and will +likely be assumed to define actual comparisons by functions that use these +traits as bounds. Many functions and macros in the standard library can then +use that assumption (although not to ensure safety). Unlike the arithmetic +and logical operators above, these operators implicitly take shared borrows +of their operands, evaluating them in [place expression context][place expression]: ```rust # let a = 1; @@ -409,20 +408,35 @@ halfway between two floating point numbers. > _AssignmentExpression_ :\ >    [_Expression_] `=` [_Expression_] -An _assignment expression_ consists of a [place expression] followed by an -equals sign (`=`) and a [value expression]. Such an expression always has -the [`unit` type]. +An *assignment expression* moves a value into a specified place. -Evaluating an assignment expression [drops](../destructors.md) the left-hand -operand, unless it's an uninitialized local variable or field of a local variable, -and [either copies or moves](../expressions.md#moved-and-copied-types) its -right-hand operand to its left-hand operand. The left-hand operand must be a -place expression: using a value expression results in a compiler error, rather +An assignment expression consists of a [mutable] [place expression], the +*assigned place operand*, followed by an equals sign (`=`) and a [value +expression], the *assigned value operand*. + +Unlike other place operands, the assigned place operand must be a place +expression. Attempting to use a value expression is a compiler error rather than promoting it to a temporary. +Evaluating assignment expressions begins by evaluating its operands. The +assigned value operand is evaluated first, followed by the assigned place +operand. + +> **Note**: This is different than other expressions in that the right operand +> is evaluated before the left one. + +It then has the effect of first [dropping] the value at the assigned place, +unless the place is an uninitialized local variable or an uninitialized field of +a local variable. Next it either [copies or moves] the assigned value to the +assigned place. + +An assignment expression always produces [the unit value][unit]. + +Example: + ```rust -# let mut x = 0; -# let y = 0; +let mut x = 0; +let y = 0; x = y; ``` @@ -441,26 +455,80 @@ x = y; >    | [_Expression_] `<<=` [_Expression_]\ >    | [_Expression_] `>>=` [_Expression_] -The `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `<<`, and `>>` operators may be -composed with the `=` operator. The expression `place_exp OP= value` is -equivalent to `place_expr = place_expr OP val`. For example, `x = x + 1` may be -written as `x += 1`. Any such expression always has the [`unit` type]. -These operators can all be overloaded using the trait with the same name as for -the normal operation followed by 'Assign', for example, `std::ops::AddAssign` -is used to overload `+=`. As with `=`, `place_expr` must be a [place -expression]. +*Compound assignment expressions* combine arithmetic and logical binary +operators with assignment expressions. + +For example: + +```rust +let mut x = 5; +x += 1; +assert!(x == 6); +``` + +The syntax of compound assignment is a [mutable] [place expression], the +*assigned operand*, then one of the operators followed by an `=` as a single +token (no whitespace), and then a [value expression], the *modifying operand*. + +Unlike other place operands, the assigned place operand must be a place +expression. Attempting to use a value expression is a compiler error rather +than promoting it to a temporary. + +Evaluation of compound assignment expressions depends on the types of the +operators. + +If both types are primitives, then the modifying operand will be evaluated +first followed by the assigned operand. It will then set the value of the +assigned operand's place to the value of performing the operation of the +operator with the values of the assigned operand and modifying operand. + +> **Note**: This is different than other expressions in that the right operand +> is evaluated before the left one. + +Otherwise, this expression is syntactic sugar for calling the function of the +overloading compound assigment trait of the operator (see the table earlier in +this chapter). A mutable borrow of the assigned operand is automatically taken. + +For example, the following expression statements in `example` are equivalent: ```rust -let mut x = 10; -x += 4; -assert_eq!(x, 14); +# struct Addable; +# use std::ops::AddAssign; + +impl AddAssign for Addable { + /* */ +# fn add_assign(&mut self, other: Addable) {} +} + +fn example() { +# let (mut a1, a2) = (Addable, Addable); + a1 += a2; + +# let (mut a1, a2) = (Addable, Addable); + AddAssign::add_assign(&mut a1, a2); +} ``` +
+ +Warning: The evaluation order of operands swaps depending on the types of the +operands: with primitive types the right-hand side will get evaluated first, +while with non-primitive types the left-hand side will get evaluated first. +Try not to write code that depends on the evaluation order of operands in +compound assignment expressions. See [this test] for an example of using this +dependency. + +
+ +[copies or moves]: ../expressions.md#moved-and-copied-types +[dropping]: ../destructors.md +[mutable]: ../expressions.md#mutability [place expression]: ../expressions.md#place-expressions-and-value-expressions +[unit]: ../types/tuple.md [value expression]: ../expressions.md#place-expressions-and-value-expressions [temporary value]: ../expressions.md#temporaries +[this test]: https://github.com/rust-lang/rust/blob/master/src/test/ui/expr/compound-assignment/eval-order.rs [float-float]: https://github.com/rust-lang/rust/issues/15536 -[`unit` type]: ../types/tuple.md [Function pointer]: ../types/function-pointer.md [Function item]: ../types/function-item.md diff --git a/src/doc/reference/src/glossary.md b/src/doc/reference/src/glossary.md index fd204c29da..03155170c3 100644 --- a/src/doc/reference/src/glossary.md +++ b/src/doc/reference/src/glossary.md @@ -61,6 +61,13 @@ through a mechanism called ‘trait objects’. A dynamically sized type (DST) is a type without a statically known size or alignment. +### Entity + +An [*entity*] is a language construct that can be referred to in some way +within the source program, usually via a [path][paths]. Entities include +[types], [items], [generic parameters], [variable bindings], [loop labels], +[lifetimes], [fields], [attributes], and [lints]. + ### Expression An expression is a combination of values, constants, variables, operators @@ -123,6 +130,28 @@ This is not affected by applied type arguments. `struct Foo` is considered local `Vec` is not. `LocalType` is local. Type aliases do not affect locality. +### Name + +A [*name*] is an [identifier] or [lifetime or loop label] that refers to an +[entity](#entity). A *name binding* is when an entity declaration introduces +an identifier or label associated with that entity. [Paths], +identifiers, and labels are used to refer to an entity. + +### Name resolution + +[*Name resolution*] is the compile-time process of tying [paths], +[identifiers], and [labels] to [entity](#entity) declarations. + +### Namespace + +A *namespace* is a logical grouping of declared [names](#name) based on the +kind of [entity](#entity) the name refers to. Namespaces allow the occurrence +of a name in one namespace to not conflict with the same name in another +namespace. + +Within a namespace, names are organized in a hierarchy, where each level of +the hierarchy has its own collection of named entities. + ### Nominal types Types that can be referred to by a path directly. Specifically [enums], @@ -133,11 +162,22 @@ Types that can be referred to by a path directly. Specifically [enums], [Traits] that can be used as [trait objects]. Only traits that follow specific [rules][object safety] are object safe. +### Path + +A [*path*] is a sequence of one or more path segments used to refer to an +[entity](#entity) in the current scope or other levels of a +[namespace](#namespace) hierarchy. + ### Prelude Prelude, or The Rust Prelude, is a small collection of items - mostly traits - that are imported into every module of every crate. The traits in the prelude are pervasive. +### Scope + +A [*scope*] is the region of source text where a named [entity](#entity) may +be referenced with that name. + ### Scrutinee A scrutinee is the expression that is matched on in `match` expressions and @@ -216,17 +256,37 @@ example of an uninhabited type is the [never type] `!`, or an enum with no varia [alignment]: type-layout.md#size-and-alignment [associated item]: #associated-item +[attributes]: attributes.md +[*entity*]: names.md [enums]: items/enumerations.md +[fields]: expressions/field-expr.md [free item]: #free-item +[generic parameters]: items/generics.md +[identifier]: identifiers.md +[identifiers]: identifiers.md [implementation]: items/implementations.md [implementations]: items/implementations.md [inherent implementation]: items/implementations.md#inherent-implementations [item]: items.md +[items]: items.md +[labels]: tokens.md#lifetimes-and-loop-labels +[lifetime or loop label]: tokens.md#lifetimes-and-loop-labels +[lifetimes]: tokens.md#lifetimes-and-loop-labels +[lints]: attributes/diagnostics.md#lint-check-attributes +[loop labels]: tokens.md#lifetimes-and-loop-labels [method]: items/associated-items.md#methods +[*Name resolution*]: names/name-resolution.md +[*name*]: names.md +[*namespace*]: names/namespaces.md [never type]: types/never.md [object safety]: items/traits.md#object-safety +[*path*]: paths.md +[Paths]: paths.md +[*scope*]: names/scopes.md [structs]: items/structs.md [trait objects]: types/trait-object.md [traits]: items/traits.md +[types]: types.md [undefined-behavior]: behavior-considered-undefined.md [unions]: items/unions.md +[variable bindings]: patterns.md diff --git a/src/doc/reference/src/items/associated-items.md b/src/doc/reference/src/items/associated-items.md index 5434c04951..0bd9a43fbd 100644 --- a/src/doc/reference/src/items/associated-items.md +++ b/src/doc/reference/src/items/associated-items.md @@ -1,5 +1,12 @@ # Associated Items +> **Syntax**\ +> _AssociatedItem_ :\ +>    [_OuterAttribute_]\* (\ +>          [_MacroInvocationSemi_]\ +>       | ( [_Visibility_]? ( [_TypeAlias_] | [_ConstantItem_] | [_Function_] ) )\ +>    ) + *Associated Items* are the items declared in [traits] or defined in [implementations]. They are called this because they are defined on an associate type — the type in the implementation. They are a subset of the kinds of @@ -79,21 +86,6 @@ let _: f64 = f64::from_i32(42); ### Methods -> _Method_ :\ ->    [_FunctionQualifiers_] `fn` [IDENTIFIER] [_Generics_]?\ ->       `(` _SelfParam_ (`,` [_FunctionParam_])\* `,`? `)`\ ->       [_FunctionReturnType_]? [_WhereClause_]?\ ->       [_BlockExpression_] -> -> _SelfParam_ :\ ->    [_OuterAttribute_]\* ( _ShorthandSelf_ | _TypedSelf_ ) -> -> _ShorthandSelf_ :\ ->    (`&` | `&` [_Lifetime_])? `mut`? `self` -> -> _TypedSelf_ :\ ->    `mut`? `self` `:` [_Type_] - Associated functions whose first parameter is named `self` are called *methods* and may be invoked using the [method call operator], for example, `x.foo()`, as well as the usual function call notation. @@ -227,6 +219,8 @@ If a type `Item` has an associated type `Assoc` from a trait `Trait`, then associated type definition. Furthermore, if `Item` is a type parameter, then `Item::Assoc` can be used in type parameters. +Associated types must not include [generic parameters] or [where clauses]. + ```rust trait AssociatedType { // Associated type declaration @@ -340,19 +334,16 @@ fn main() { } ``` -[_BlockExpression_]: ../expressions/block-expr.md -[_FunctionParam_]: functions.md -[_FunctionQualifiers_]: functions.md -[_FunctionReturnType_]: functions.md -[_Generics_]: generics.md -[_Lifetime_]: ../trait-bounds.md -[_Type_]: ../types.md#type-expressions -[_WhereClause_]: generics.md#where-clauses +[_ConstantItem_]: constant-items.md +[_Function_]: functions.md +[_MacroInvocationSemi_]: ../macros.md#macro-invocation +[_OuterAttribute_]: ../attributes.md +[_TypeAlias_]: type-aliases.md +[_Visibility_]: ../visibility-and-privacy.md [`Arc`]: ../special-types-and-traits.md#arct [`Box`]: ../special-types-and-traits.md#boxt [`Pin

`]: ../special-types-and-traits.md#pinp [`Rc`]: ../special-types-and-traits.md#rct -[_OuterAttribute_]: ../attributes.md [traits]: traits.md [type aliases]: type-aliases.md [inherent implementations]: implementations.md#inherent-implementations @@ -367,3 +358,5 @@ fn main() { [method call operator]: ../expressions/method-call-expr.md [path]: ../paths.md [regular function parameters]: functions.md#attributes-on-function-parameters +[generic parameters]: generics.md +[where clauses]: generics.md#where-clauses diff --git a/src/doc/reference/src/items/constant-items.md b/src/doc/reference/src/items/constant-items.md index 3d8834fe80..75b586ebea 100644 --- a/src/doc/reference/src/items/constant-items.md +++ b/src/doc/reference/src/items/constant-items.md @@ -2,7 +2,7 @@ > **Syntax**\ > _ConstantItem_ :\ ->    `const` ( [IDENTIFIER] | `_` ) `:` [_Type_] `=` [_Expression_] `;` +>    `const` ( [IDENTIFIER] | `_` ) `:` [_Type_] ( `=` [_Expression_] )? `;` A *constant item* is an optionally named _[constant value]_ which is not associated with a specific memory location in the program. Constants are essentially inlined @@ -38,6 +38,8 @@ const BITS_N_STRINGS: BitsNStrings<'static> = BitsNStrings { }; ``` +The constant expression may only be omitted in a [trait definition]. + ## Constants with Destructors Constants can contain destructors. Destructors are run when the value goes out @@ -91,6 +93,7 @@ m!(const _: () = ();); [constant value]: ../const_eval.md#constant-expressions [free]: ../glossary.md#free-item [static lifetime elision]: ../lifetime-elision.md#static-lifetime-elision +[trait definition]: traits.md [IDENTIFIER]: ../identifiers.md [underscore imports]: use-declarations.md#underscore-imports [_Type_]: ../types.md#type-expressions diff --git a/src/doc/reference/src/items/enumerations.md b/src/doc/reference/src/items/enumerations.md index 7e47955083..8248a47db4 100644 --- a/src/doc/reference/src/items/enumerations.md +++ b/src/doc/reference/src/items/enumerations.md @@ -4,7 +4,7 @@ > _Enumeration_ :\ >    `enum` > [IDENTIFIER]  -> [_Generics_]? +> [_GenericParams_]? > [_WhereClause_]? > `{` _EnumItems_? `}` > @@ -172,7 +172,7 @@ enum E { ``` [IDENTIFIER]: ../identifiers.md -[_Generics_]: generics.md +[_GenericParams_]: generics.md [_WhereClause_]: generics.md#where-clauses [_Expression_]: ../expressions.md [_TupleFields_]: structs.md diff --git a/src/doc/reference/src/items/extern-crates.md b/src/doc/reference/src/items/extern-crates.md index e7dc3228bc..7c2f9ad762 100644 --- a/src/doc/reference/src/items/extern-crates.md +++ b/src/doc/reference/src/items/extern-crates.md @@ -12,8 +12,10 @@ An _`extern crate` declaration_ specifies a dependency on an external crate. The external crate is then bound into the declaring scope as the [identifier] -provided in the `extern crate` declaration. The `as` clause can be used to -bind the imported crate to a different name. +provided in the `extern crate` declaration. Additionally, if the `extern +crate` appears in the crate root, then the crate name is also added to the +[extern prelude], making it automatically in scope in all modules. The `as` +clause can be used to bind the imported crate to a different name. The external crate is resolved to a specific `soname` at compile time, and a runtime linkage requirement to that `soname` is passed to the linker for @@ -52,39 +54,8 @@ extern crate hello_world; // hyphen replaced with an underscore ## Extern Prelude -External crates imported with `extern crate` in the root module or provided to -the compiler (as with the `--extern` flag with `rustc`) are added to the -"extern prelude". Crates in the extern prelude are in scope in the entire -crate, including inner modules. If imported with `extern crate orig_name as -new_name`, then the symbol `new_name` is instead added to the prelude. - -The `core` crate is always added to the extern prelude. The `std` crate -is added as long as the [`no_std`] attribute is not specified in the crate root. - -The [`no_implicit_prelude`] attribute can be used on a module to disable -prelude lookups within that module. - -> **Edition Differences**: In the 2015 edition, crates in the extern prelude -> cannot be referenced via [use declarations], so it is generally standard -> practice to include `extern crate` declarations to bring them into scope. -> -> Beginning in the 2018 edition, [use declarations] can reference crates in -> the extern prelude, so it is considered unidiomatic to use `extern crate`. - -> **Note**: Additional crates that ship with `rustc`, such as [`alloc`], and -> [`test`], are not automatically included with the `--extern` flag when using -> Cargo. They must be brought into scope with an `extern crate` declaration, -> even in the 2018 edition. -> -> ```rust -> extern crate alloc; -> use alloc::rc::Rc; -> ``` - - +This section has been moved to [Preludes — Extern Prelude](../names/preludes.md#extern-prelude). + ## Underscore Imports @@ -94,7 +65,7 @@ useful for crates that only need to be linked, but are never referenced, and will avoid being reported as unused. The [`macro_use` attribute] works as usual and import the macro names -into the macro-use prelude. +into the [`macro_use` prelude]. ## The `no_link` attribute @@ -105,8 +76,19 @@ crate to access only its macros. [IDENTIFIER]: ../identifiers.md [RFC 940]: https://github.com/rust-lang/rfcs/blob/master/text/0940-hyphens-considered-harmful.md [`macro_use` attribute]: ../macros-by-example.md#the-macro_use-attribute -[`alloc`]: https://doc.rust-lang.org/alloc/ -[`no_implicit_prelude`]: modules.md#prelude-items -[`no_std`]: ../crates-and-source-files.md#preludes-and-no_std -[`test`]: https://doc.rust-lang.org/test/ -[use declarations]: use-declarations.md +[extern prelude]: ../names/preludes.md#extern-prelude +[`macro_use` prelude]: ../names/preludes.md#macro_use-prelude + + diff --git a/src/doc/reference/src/items/external-blocks.md b/src/doc/reference/src/items/external-blocks.md index b31dd17d48..9443276d4b 100644 --- a/src/doc/reference/src/items/external-blocks.md +++ b/src/doc/reference/src/items/external-blocks.md @@ -10,25 +10,8 @@ > _ExternalItem_ :\ >    [_OuterAttribute_]\* (\ >          [_MacroInvocationSemi_]\ ->       | ( [_Visibility_]? ( _ExternalStaticItem_ | _ExternalFunctionItem_ ) )\ +>       | ( [_Visibility_]? ( [_StaticItem_] | [_Function_] ) )\ >    ) -> -> _ExternalStaticItem_ :\ ->    `static` `mut`? [IDENTIFIER] `:` [_Type_] `;` -> -> _ExternalFunctionItem_ :\ ->    `fn` [IDENTIFIER] [_Generics_]?\ ->    `(` ( _NamedFunctionParameters_ | _NamedFunctionParametersWithVariadics_ )? `)`\ ->    [_FunctionReturnType_]? [_WhereClause_]? `;` -> -> _NamedFunctionParameters_ :\ ->    _NamedFunctionParam_ ( `,` _NamedFunctionParam_ )\* `,`? -> -> _NamedFunctionParam_ :\ ->    [_OuterAttribute_]\* ( [IDENTIFIER] | `_` ) `:` [_Type_] -> -> _NamedFunctionParametersWithVariadics_ :\ ->    ( _NamedFunctionParam_ `,` )\* _NamedFunctionParam_ `,` [_OuterAttribute_]\* `...` External blocks provide _declarations_ of items that are not _defined_ in the current crate and are the basis of Rust's foreign function interface. These are @@ -46,9 +29,10 @@ token stream. ## Functions Functions within external blocks are declared in the same way as other Rust -functions, with the exception that they may not have a body and are instead +functions, with the exception that they must not have a body and are instead terminated by a semicolon. Patterns are not allowed in parameters, only -[IDENTIFIER] or `_` may be used. +[IDENTIFIER] or `_` may be used. Function qualifiers (`const`, `async`, +`unsafe`, and `extern`) are not allowed. Functions within external blocks may be called by Rust code, just like functions defined in Rust. The Rust compiler automatically translates between @@ -109,12 +93,15 @@ There are also some platform-specific ABI strings: ## Variadic functions -Functions within external blocks may be variadic by specifying `...` after one -or more named arguments in the argument list: +Functions within external blocks may be variadic by specifying `...` as the +last argument. There must be at least one parameter before the variadic +parameter. The variadic parameter may optionally be specified with an +identifier. ```rust -extern { +extern "C" { fn foo(x: i32, ...); + fn with_name(format: *const u8, args: ...); } ``` @@ -189,15 +176,13 @@ restrictions as [regular function parameters]. [functions]: functions.md [statics]: static-items.md [_Abi_]: functions.md -[_FunctionReturnType_]: functions.md -[_Generics_]: generics.md +[_Function_]: functions.md [_InnerAttribute_]: ../attributes.md [_MacroInvocationSemi_]: ../macros.md#macro-invocation [_MetaListNameValueStr_]: ../attributes.md#meta-item-attribute-syntax [_MetaNameValueStr_]: ../attributes.md#meta-item-attribute-syntax [_OuterAttribute_]: ../attributes.md -[_Type_]: ../types.md#type-expressions +[_StaticItem_]: static-items.md [_Visibility_]: ../visibility-and-privacy.md -[_WhereClause_]: generics.md#where-clauses [attributes]: ../attributes.md [regular function parameters]: functions.md#attributes-on-function-parameters diff --git a/src/doc/reference/src/items/functions.md b/src/doc/reference/src/items/functions.md index 27dbef24b4..b6bd159046 100644 --- a/src/doc/reference/src/items/functions.md +++ b/src/doc/reference/src/items/functions.md @@ -2,28 +2,45 @@ > **Syntax**\ > _Function_ :\ ->    _FunctionQualifiers_ `fn` [IDENTIFIER] [_Generics_]?\ +>    _FunctionQualifiers_ `fn` [IDENTIFIER] [_GenericParams_]?\ >       `(` _FunctionParameters_? `)`\ >       _FunctionReturnType_? [_WhereClause_]?\ ->       [_BlockExpression_] +>       ( [_BlockExpression_] | `;` ) > > _FunctionQualifiers_ :\ ->    _AsyncConstQualifiers_? `unsafe`? (`extern` _Abi_?)? -> -> _AsyncConstQualifiers_ :\ ->    `async` | `const` +>    `const`? `async`[^async-edition]? `unsafe`? (`extern` _Abi_?)? > > _Abi_ :\ >    [STRING_LITERAL] | [RAW_STRING_LITERAL] > > _FunctionParameters_ :\ ->    _FunctionParam_ (`,` _FunctionParam_)\* `,`? +>       _SelfParam_ `,`?\ +>    | (_SelfParam_ `,`)? _FunctionParam_ (`,` _FunctionParam_)\* `,`? +> +> _SelfParam_ :\ +>    [_OuterAttribute_]\* ( _ShorthandSelf_ | _TypedSelf_ ) +> +> _ShorthandSelf_ :\ +>    (`&` | `&` [_Lifetime_])? `mut`? `self` +> +> _TypedSelf_ :\ +>    `mut`? `self` `:` [_Type_] > > _FunctionParam_ :\ ->    [_OuterAttribute_]\* [_Pattern_] `:` [_Type_] +>    [_OuterAttribute_]\* ( +> _FunctionParamPattern_ | `...` | [_Type_] [^fn-param-2015] +> ) +> +> _FunctionParamPattern_ :\ +>    [_Pattern_] `:` ( [_Type_] | `...` ) > > _FunctionReturnType_ :\ >    `->` [_Type_] +> +> [^async-edition]: The `async` qualifier is not allowed in the 2015 edition. +> +> [^fn-param-2015]: Function parameters with only a type are only allowed +> in an associated function of a [trait item] in the 2015 edition. A _function_ consists of a [block], along with a name and a set of parameters. Other than a name, all these are optional. Functions are declared with the @@ -43,13 +60,25 @@ fn answer_to_life_the_universe_and_everything() -> i32 { } ``` -As with `let` bindings, function arguments are irrefutable [patterns], so any +## Function parameters + +As with `let` bindings, function parameters are irrefutable [patterns], so any pattern that is valid in a let binding is also valid as an argument: ```rust fn first((value, _): (i32, i32)) -> i32 { value } ``` +If the first parameter is a _SelfParam_, this indicates that the function is a +[method]. Functions with a self parameter may only appear as an [associated +function] in a [trait] or [implementation]. + +A parameter with the `...` token indicates a [variadic function], and may only +be used as the last parameter of a [external block] function. The variadic +parameter may have an optional identifier, such as `args: ...`. + +## Function body + The block of a function is conceptually wrapped in a block that binds the argument patterns and then `return`s the value of the function's block. This means that the tail expression of the block, if evaluated, ends up being @@ -67,6 +96,9 @@ return { }; ``` +Functions without a body block are terminated with a semicolon. This form +may only appear in a [trait] or [external block]. + ## Generic functions A _generic function_ allows one or more _parameterized types_ to appear in its @@ -187,6 +219,9 @@ Functions qualified with the `const` keyword are [const functions], as are [tuple struct] and [tuple variant] constructors. _Const functions_ can be called from within [const context]s. +Const functions are not allowed to be [async](#async-functions), and cannot +use the [`extern` function qualifier](#extern-function-qualifier). + ## Async functions Functions may be qualified as async, and this can also be combined with the @@ -341,7 +376,8 @@ fn foo_oof(#[some_inert_attribute] arg: u8) { [RAW_STRING_LITERAL]: ../tokens.md#raw-string-literals [STRING_LITERAL]: ../tokens.md#string-literals [_BlockExpression_]: ../expressions/block-expr.md -[_Generics_]: generics.md +[_GenericParams_]: generics.md +[_Lifetime_]: ../trait-bounds.md [_Pattern_]: ../patterns.md [_Type_]: ../types.md#type-expressions [_WhereClause_]: generics.md#where-clauses @@ -372,3 +408,8 @@ fn foo_oof(#[some_inert_attribute] arg: u8) { [`link_section`]: ../abi.md#the-link_section-attribute [`no_mangle`]: ../abi.md#the-no_mangle-attribute [built-in attributes]: ../attributes.html#built-in-attributes-index +[trait item]: traits.md +[method]: associated-items.md#methods +[associated function]: associated-items.md#associated-functions-and-methods +[implementation]: implementations.md +[variadic function]: external-blocks.md#variadic-functions diff --git a/src/doc/reference/src/items/generics.md b/src/doc/reference/src/items/generics.md index ba35e64d25..a7de436ec3 100644 --- a/src/doc/reference/src/items/generics.md +++ b/src/doc/reference/src/items/generics.md @@ -1,43 +1,212 @@ -# Type and Lifetime Parameters +# Generic parameters > **Syntax**\ -> _Generics_ :\ ->    `<` _GenericParams_ `>` -> > _GenericParams_ :\ ->       _LifetimeParams_\ ->    | ( _LifetimeParam_ `,` )\* _TypeParams_ +>       `<` `>`\ +>    | `<` (_GenericParam_ `,`)\* _GenericParam_ `,`? `>` > -> _LifetimeParams_ :\ ->    ( _LifetimeParam_ `,` )\* _LifetimeParam_? +> _GenericParam_ :\ +>    [_OuterAttribute_]\* ( _LifetimeParam_ | _TypeParam_ | _ConstParam_ ) > > _LifetimeParam_ :\ ->    [_OuterAttribute_]? [LIFETIME_OR_LABEL] ( `:` [_LifetimeBounds_] )? -> -> _TypeParams_:\ ->    ( _TypeParam_ `,` )\* _TypeParam_? +>    [LIFETIME_OR_LABEL] ( `:` [_LifetimeBounds_] )? > > _TypeParam_ :\ ->    [_OuterAttribute_]? [IDENTIFIER] ( `:` [_TypeParamBounds_]? )? ( `=` [_Type_] )? +>    [IDENTIFIER]( `:` [_TypeParamBounds_]? )? ( `=` [_Type_] )? +> +> _ConstParam_:\ +>    `const` [IDENTIFIER] `:` [_Type_] -Functions, type aliases, structs, enumerations, unions, traits, and -implementations may be *parameterized* by types and lifetimes. These parameters -are listed in angle brackets (`<...>`), +[Functions], [type aliases], [structs], [enumerations], [unions], [traits], and +[implementations] may be *parameterized* by types, constants, and lifetimes. These +parameters are listed in angle brackets (`<...>`), usually immediately after the name of the item and before its definition. For implementations, which don't have a name, they come directly after `impl`. -Lifetime parameters must be declared before type parameters. Some examples of -items with type and lifetime parameters: +The order of generic parameters is restricted to lifetime parameters, then type parameters, and then const parameters. + +Some examples of items with type, const, and lifetime parameters: ```rust fn foo<'a, T>() {} trait A {} struct Ref<'a, T> where T: 'a { r: &'a T } +struct InnerArray([T; N]); ``` +Generic parameters are in scope within the item definition where they are +declared. They are not in scope for items declared within the body of a +function as described in [item declarations]. + [References], [raw pointers], [arrays], [slices][arrays], [tuples], and [function pointers] have lifetime or type parameters as well, but are not referred to with path syntax. +### Const generics + +*Const generic parameters* allow items to be generic over constant values. The +const identifier introduces a name for the constant parameter, and all +instances of the item must be instantiated with a value of the given type. + + + +The only allowed types of const parameters are `u8`, `u16`, `u32`, `u64`, `u128`, `usize` +`i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `char` and `bool`. + +Const parameters can be used anywhere a [const item] can be used, with the +exception that when used in a [type] or [array repeat expression], it must be +standalone (as described below). That is, they are allowed in the following +places: + +1. As an applied const to any type which forms a part of the signature of the + item in question. +2. As part of a const expression used to define an [associated const], or as a + parameter to an [associated type]. +3. As a value in any runtime expression in the body of any functions in the + item. +4. As a parameter to any type used in the body of any functions in the item. +5. As a part of the type of any fields in the item. + +```rust +// Examples where const generic parameters can be used. + +// Used in the signature of the item itself. +fn foo(arr: [i32; N]) { + // Used as a type within a function body. + let x: [i32; N]; + // Used as an expression. + println!("{}", N * 2); +} + +// Used as a field of a struct. +struct Foo([i32; N]); + +impl Foo { + // Used as an associated constant. + const CONST: usize = N * 4; +} + +trait Trait { + type Output; +} + +impl Trait for Foo { + // Used as an associated type. + type Output = [i32; N]; +} +``` + +```rust,compile_fail +// Examples where const generic parameters cannot be used. +fn foo() { + // Cannot use in item definitions within a function body. + const BAD_CONST: [usize; N] = [1; N]; + static BAD_STATIC: [usize; N] = [1; N]; + fn inner(bad_arg: [usize; N]) { + let bad_value = N * 2; + } + type BadAlias = [usize; N]; + struct BadStruct([usize; N]); +} +``` + +As a further restriction, const parameters may only appear as a standalone +argument inside of a [type] or [array repeat expression]. In those contexts, +they may only be used as a single segment [path expression], possibly inside a +[block] (such as `N` or `{N}`). That is, they cannot be combined with other +expressions. + +```rust,compile_fail +// Examples where const parameters may not be used. + +// Not allowed to combine in other expressions in types, such as the +// arithmetic expression in the return type here. +fn bad_function() -> [u8; {N + 1}] { + // Similarly not allowed for array repeat expressions. + [1; {N + 1}] +} +``` + +A const argument in a [path] specifies the const value to use for that item. +The argument must be a [const expression] of the type ascribed to the const +parameter. The const expression must be a [block expression][block] +(surrounded with braces) unless it is a single path segment (an [IDENTIFIER]) +or a [literal] (with a possibly leading `-` token). + +> **Note**: This syntactic restriction is necessary to avoid requiring +> infinite lookahead when parsing an expression inside of a type. + +```rust +fn double() { + println!("doubled: {}", N * 2); +} + +const SOME_CONST: i32 = 12; + +fn example() { + // Example usage of a const argument. + double::<9>(); + double::<-123>(); + double::<{7 + 8}>(); + double::(); + double::<{ SOME_CONST + 5 }>(); +} +``` + +When there is ambiguity if a generic argument could be resolved as either a +type or const argument, it is always resolved as a type. Placing the argument +in a block expression can force it to be interpreted as a const argument. + + + +```rust,compile_fail +type N = u32; +struct Foo; +// The following is an error, because `N` is interpreted as the type alias `N`. +fn foo() -> Foo { todo!() } // ERROR +// Can be fixed by wrapping in braces to force it to be interprted as the `N` +// const parameter: +fn bar() -> Foo<{ N }> { todo!() } // ok +``` + +Unlike type and lifetime parameters, const parameters can be declared without +being used inside of a parameterized item, with the exception of +implementations as described in [generic implementations]: + +```rust,compile_fail +// ok +struct Foo; +enum Bar { A, B } + +// ERROR: unused parameter +struct Baz; +struct Biz<'a>; +struct Unconstrained; +impl Unconstrained {} +``` + +When resolving a trait bound obligation, the exhaustiveness of all +implementations of const parameters is not considered when determining if the +bound is satisfied. For example, in the following, even though all possible +const values for the `bool` type are implemented, it is still an error that +the trait bound is not satisfied: + +```rust,compile_fail +struct Foo; +trait Bar {} +impl Bar for Foo {} +impl Bar for Foo {} + +fn needs_bar(_: impl Bar) {} +fn generic() { + let v = Foo::; + needs_bar(v); // ERROR: trait bound `Foo: Bar` is not satisfied +} +``` + + ## Where clauses > **Syntax**\ @@ -55,17 +224,20 @@ referred to with path syntax. >    _ForLifetimes_? [_Type_] `:` [_TypeParamBounds_]? > > _ForLifetimes_ :\ ->    `for` `<` [_LifetimeParams_](#type-and-lifetime-parameters) `>` +>    `for` [_GenericParams_](#generic-parameters) *Where clauses* provide another way to specify bounds on type and lifetime parameters as well as a way to specify bounds on types that aren't type parameters. -Bounds that don't use the item's parameters or higher-ranked lifetimes are +The `for` keyword can be used to introduce [higher-ranked lifetimes]. It only +allows [_LifetimeParam_] parameters. + +Bounds that don't use the item's parameters or [higher-ranked lifetimes] are checked when the item is defined. It is an error for such a bound to be false. [`Copy`], [`Clone`], and [`Sized`] bounds are also checked for certain generic -types when defining the item. It is an error to have `Copy` or `Clone`as a +types when defining the item. It is an error to have `Copy` or `Clone` as a bound on a mutable reference, [trait object] or [slice][arrays] or `Sized` as a bound on a trait object or slice. @@ -105,19 +277,42 @@ struct Foo<#[my_flexible_clone(unbounded)] H> { [IDENTIFIER]: ../identifiers.md [LIFETIME_OR_LABEL]: ../tokens.md#lifetimes-and-loop-labels +[_LifetimeParam_]: #generic-parameters [_LifetimeBounds_]: ../trait-bounds.md [_Lifetime_]: ../trait-bounds.md [_OuterAttribute_]: ../attributes.md [_Type_]: ../types.md#type-expressions [_TypeParamBounds_]: ../trait-bounds.md +[array repeat expression]: ../expressions/array-expr.md [arrays]: ../types/array.md +[associated const]: associated-items.md#associated-constants +[associated type]: associated-items.md#associated-types +[block]: ../expressions/block-expr.md +[const contexts]: ../const_eval.md#const-context +[const expression]: ../const_eval.md#constant-expressions +[const item]: constant-items.md +[enumerations]: enumerations.md +[functions]: functions.md [function pointers]: ../types/function-pointer.md -[references]: ../types/pointer.md#shared-references- +[generic implementations]: implementations.md#generic-implementations +[higher-ranked lifetimes]: ../trait-bounds.md#higher-ranked-trait-bounds +[implementations]: implementations.md +[item declarations]: ../statements.md#item-declarations +[item]: ../items.md +[literal]: ../expressions/literal-expr.md +[path]: ../paths.md +[path expression]: ../expressions/path-expr.md [raw pointers]: ../types/pointer.md#raw-pointers-const-and-mut +[references]: ../types/pointer.md#shared-references- [`Clone`]: ../special-types-and-traits.md#clone [`Copy`]: ../special-types-and-traits.md#copy [`Sized`]: ../special-types-and-traits.md#sized +[structs]: structs.md [tuples]: ../types/tuple.md [trait object]: ../types/trait-object.md +[traits]: traits.md +[type aliases]: type-aliases.md +[type]: ../types.md +[unions]: unions.md [attributes]: ../attributes.md diff --git a/src/doc/reference/src/items/implementations.md b/src/doc/reference/src/items/implementations.md index 26ba45a8e2..f1baad74e0 100644 --- a/src/doc/reference/src/items/implementations.md +++ b/src/doc/reference/src/items/implementations.md @@ -5,31 +5,19 @@ >    _InherentImpl_ | _TraitImpl_ > > _InherentImpl_ :\ ->    `impl` [_Generics_]? [_Type_] [_WhereClause_]? `{`\ +>    `impl` [_GenericParams_]? [_Type_] [_WhereClause_]? `{`\ >       [_InnerAttribute_]\*\ ->       _InherentImplItem_\*\ +>       [_AssociatedItem_]\*\ >    `}` > -> _InherentImplItem_ :\ ->    [_OuterAttribute_]\* (\ ->          [_MacroInvocationSemi_]\ ->       | ( [_Visibility_]? ( [_ConstantItem_] | [_Function_] | [_Method_] ) )\ ->    ) -> > _TraitImpl_ :\ ->    `unsafe`? `impl` [_Generics_]? `!`? +>    `unsafe`? `impl` [_GenericParams_]? `!`? > [_TypePath_] `for` [_Type_]\ >    [_WhereClause_]?\ >    `{`\ >       [_InnerAttribute_]\*\ ->       _TraitImplItem_\*\ +>       [_AssociatedItem_]\*\ >    `}` -> -> _TraitImplItem_ :\ ->    [_OuterAttribute_]\* (\ ->          [_MacroInvocationSemi_]\ ->       | ( [_Visibility_]? ( [_TypeAlias_] | [_ConstantItem_] | [_Function_] | [_Method_] ) )\ ->    ) An _implementation_ is an item that associates items with an _implementing type_. Implementations are defined with the keyword `impl` and contain functions @@ -52,7 +40,7 @@ the _associated items_ to the implementing type. Inherent implementations associate the contained items to the implementing type. Inherent implementations can contain [associated -functions] (including methods) and [associated constants]. They cannot +functions] (including [methods]) and [associated constants]. They cannot contain associated type aliases. The [path] to an associated item is any path to the implementing type, @@ -180,11 +168,9 @@ is considered local. ## Generic Implementations -An implementation can take type and lifetime parameters, which can be used in -the rest of the implementation. Type parameters declared for an implementation -must be used at least once in either the trait or the implementing type of an -implementation. Implementation parameters are written directly after the `impl` -keyword. +An implementation can take [generic parameters], which can be used in the rest +of the implementation. Implementation parameters are written directly after the +`impl` keyword. ```rust # trait Seq { fn dummy(&self, _: T) { } } @@ -196,6 +182,85 @@ impl Seq for u32 { } ``` +Generic parameters *constrain* an implementation if the parameter appears at +least once in one of: + +* The implemented trait, if it has one +* The implementing type +* As an [associated type] in the [bounds] of a type that contains another + parameter that constrains the implementation + +Type and const parameters must always constrain the implementation. Lifetimes +must constrain the implementation if the lifetime is used in an associated type. + +Examples of constraining situations: + +```rust +# trait Trait{} +# trait GenericTrait {} +# trait HasAssocType { type Ty; } +# struct Struct; +# struct GenericStruct(T); +# struct ConstGenericStruct([(); N]); +// T constrains by being an argument to GenericTrait. +impl GenericTrait for i32 { /* ... */ } + +// T constrains by being an arguement to GenericStruct +impl Trait for GenericStruct { /* ... */ } + +// Likewise, N constrains by being an argument to ConstGenericStruct +impl Trait for ConstGenericStruct { /* ... */ } + +// T constrains by being in an associated type in a bound for type `U` which is +// itself a generic parameter constraining the trait. +impl GenericTrait for u32 where U: HasAssocType { /* ... */ } + +// Like previous, except the type is `(U, isize)`. `U` appears inside the type +// that includes `T`, and is not the type itself. +impl GenericStruct where (U, isize): HasAssocType { /* ... */ } +``` + +Examples of non-constraining situations: + +```rust,compile_fail +// The rest of these are errors, since they have type or const parameters that +// do not constrain. + +// T does not constrain since it does not appear at all. +impl Struct { /* ... */ } + +// N does not constrain for the same reason. +impl Struct { /* ... */ } + +// Usage of T inside the implementation does not constrain the impl. +impl Struct { + fn uses_t(t: &T) { /* ... */ } +} + +// T is used as an associated type in the bounds for U, but U does not constrain. +impl Struct where U: HasAssocType { /* ... */ } + +// T is used in the bounds, but not as an associated type, so it does not constrain. +impl GenericTrait for u32 where U: GenericTrait {} +``` + +Example of an allowed unconstraining lifetime parameter: + +```rust +# struct Struct; +impl<'a> Struct {} +``` + +Example of a disallowed unconstraining lifetime parameter: + +```rust,compile_fail +# struct Struct; +# trait HasAssocType { type Ty; } +impl<'a> HasAssocType for Struct { + type Ty = &'a Struct; +} +``` + ## Attributes on Implementations Implementations may contain outer [attributes] before the `impl` keyword and @@ -204,25 +269,23 @@ attributes must come before any associated items. That attributes that have meaning here are [`cfg`], [`deprecated`], [`doc`], and [the lint check attributes]. -[_ConstantItem_]: constant-items.md -[_Function_]: functions.md -[_Generics_]: generics.md +[_AssociatedItem_]: associated-items.md +[_GenericParams_]: generics.md [_InnerAttribute_]: ../attributes.md -[_MacroInvocationSemi_]: ../macros.md#macro-invocation -[_Method_]: associated-items.md#methods -[_OuterAttribute_]: ../attributes.md -[_TypeAlias_]: type-aliases.md [_TypePath_]: ../paths.md#paths-in-types [_Type_]: ../types.md#type-expressions -[_Visibility_]: ../visibility-and-privacy.md [_WhereClause_]: generics.md#where-clauses [trait]: traits.md -[associated functions]: associated-items.md#associated-functions-and-methods [associated constants]: associated-items.md#associated-constants +[associated functions]: associated-items.md#associated-functions-and-methods +[associated type]: associated-items.md#associated-types [attributes]: ../attributes.md +[bounds]: ../trait-bounds.md [`cfg`]: ../conditional-compilation.md [`deprecated`]: ../attributes/diagnostics.md#the-deprecated-attribute [`doc`]: ../../rustdoc/the-doc-attribute.html +[generic parameters]: generics.md +[methods]: associated-items.md#methods [path]: ../paths.md [the lint check attributes]: ../attributes/diagnostics.md#lint-check-attributes [Unsafe traits]: traits.md#unsafe-traits diff --git a/src/doc/reference/src/items/modules.md b/src/doc/reference/src/items/modules.md index b688665c0f..ff9cae078c 100644 --- a/src/doc/reference/src/items/modules.md +++ b/src/doc/reference/src/items/modules.md @@ -128,15 +128,6 @@ mod thread { } ``` -## Prelude Items - -Modules implicitly have some names in scope. These name are to built-in types, -macros imported with [`#[macro_use]`][macro_use] on an extern crate, and by the crate's -[prelude]. These names are all made of a single identifier. These names are not -part of the module, so for example, any name `name`, `self::name` is not a -valid path. The names added by the [prelude] can be removed by placing the -`no_implicit_prelude` [attribute] onto the module or one of its ancestor modules. - ## Attributes on Modules Modules, like all items, accept outer attributes. They also accept inner @@ -144,18 +135,32 @@ attributes: either after `{` for a module with a body, or at the beginning of th source file, after the optional BOM and shebang. The built-in attributes that have meaning on a module are [`cfg`], -[`deprecated`], [`doc`], [the lint check attributes], `path`, and -`no_implicit_prelude`. Modules also accept macro attributes. +[`deprecated`], [`doc`], [the lint check attributes], [`path`], and +[`no_implicit_prelude`]. Modules also accept macro attributes. [_InnerAttribute_]: ../attributes.md [_Item_]: ../items.md -[macro_use]: ../macros-by-example.md#the-macro_use-attribute [`cfg`]: ../conditional-compilation.md [`deprecated`]: ../attributes/diagnostics.md#the-deprecated-attribute [`doc`]: ../../rustdoc/the-doc-attribute.html +[`no_implicit_prelude`]: ../names/preludes.md#the-no_implicit_prelude-attribute +[`path`]: #the-path-attribute [IDENTIFIER]: ../identifiers.md [attribute]: ../attributes.md [items]: ../items.md [module path]: ../paths.md -[prelude]: ../crates-and-source-files.md#preludes-and-no_std [the lint check attributes]: ../attributes/diagnostics.md#lint-check-attributes + + diff --git a/src/doc/reference/src/items/static-items.md b/src/doc/reference/src/items/static-items.md index 4e9640e4ea..284e78e281 100644 --- a/src/doc/reference/src/items/static-items.md +++ b/src/doc/reference/src/items/static-items.md @@ -3,7 +3,7 @@ > **Syntax**\ > _StaticItem_ :\ >    `static` `mut`? [IDENTIFIER] `:` [_Type_] -> `=` [_Expression_] `;` +> ( `=` [_Expression_] )? `;` A *static item* is similar to a [constant], except that it represents a precise memory location in the program. All references to the static refer to the same @@ -23,6 +23,9 @@ statics: * The type must have the `Sync` trait bound to allow thread-safe access. * Constants cannot refer to statics. +The initializer expression must be omitted in an [external block], and must be +provided for free static items. + ## Mutable statics If a static item is declared with the `mut` keyword, then it is allowed to be @@ -73,6 +76,7 @@ following are true: [constant]: constant-items.md [`drop`]: ../destructors.md [constant expression]: ../const_eval.md#constant-expressions +[external block]: external-blocks.md [interior mutable]: ../interior-mutability.md [IDENTIFIER]: ../identifiers.md [_Type_]: ../types.md#type-expressions diff --git a/src/doc/reference/src/items/structs.md b/src/doc/reference/src/items/structs.md index debd52cc78..9523e7bfb9 100644 --- a/src/doc/reference/src/items/structs.md +++ b/src/doc/reference/src/items/structs.md @@ -8,14 +8,14 @@ > _StructStruct_ :\ >    `struct` > [IDENTIFIER]  -> [_Generics_]? +> [_GenericParams_]? > [_WhereClause_]? > ( `{` _StructFields_? `}` | `;` ) > > _TupleStruct_ :\ >    `struct` > [IDENTIFIER]  -> [_Generics_]? +> [_GenericParams_]? > `(` _TupleFields_? `)` > [_WhereClause_]? > `;` @@ -82,7 +82,7 @@ particular layout using the [`repr` attribute]. [_OuterAttribute_]: ../attributes.md [IDENTIFIER]: ../identifiers.md -[_Generics_]: generics.md +[_GenericParams_]: generics.md [_WhereClause_]: generics.md#where-clauses [_Visibility_]: ../visibility-and-privacy.md [_Type_]: ../types.md#type-expressions diff --git a/src/doc/reference/src/items/traits.md b/src/doc/reference/src/items/traits.md index d08f45c6f7..e11e1e8de4 100644 --- a/src/doc/reference/src/items/traits.md +++ b/src/doc/reference/src/items/traits.md @@ -3,49 +3,12 @@ > **Syntax**\ > _Trait_ :\ >    `unsafe`? `trait` [IDENTIFIER]  -> [_Generics_]? +> [_GenericParams_]? > ( `:` [_TypeParamBounds_]? )? > [_WhereClause_]? `{`\ >      [_InnerAttribute_]\*\ ->      _TraitItem_\*\ +>      [_AssociatedItem_]\*\ >    `}` -> -> _TraitItem_ :\ ->    [_OuterAttribute_]\* [_Visibility_]? (\ ->          _TraitFunc_\ ->       | _TraitMethod_\ ->       | _TraitConst_\ ->       | _TraitType_\ ->       | [_MacroInvocationSemi_]\ ->    ) -> -> _TraitFunc_ :\ ->       _TraitFunctionDecl_ ( `;` | [_BlockExpression_] ) -> -> _TraitMethod_ :\ ->       _TraitMethodDecl_ ( `;` | [_BlockExpression_] ) -> -> _TraitFunctionDecl_ :\ ->    [_FunctionQualifiers_] `fn` [IDENTIFIER] [_Generics_]?\ ->       `(` _TraitFunctionParameters_? `)`\ ->       [_FunctionReturnType_]? [_WhereClause_]? -> -> _TraitMethodDecl_ :\ ->    [_FunctionQualifiers_] `fn` [IDENTIFIER] [_Generics_]?\ ->       `(` [_SelfParam_] (`,` _TraitFunctionParam_)\* `,`? `)`\ ->       [_FunctionReturnType_]? [_WhereClause_]? -> -> _TraitFunctionParameters_ :\ ->    _TraitFunctionParam_ (`,` _TraitFunctionParam_)\* `,`? -> -> _TraitFunctionParam_[†](#parameter-patterns) :\ ->    [_OuterAttribute_]\* ( [_Pattern_] `:` )? [_Type_] -> -> _TraitConst_ :\ ->    `const` [IDENTIFIER] `:` [_Type_] ( `=` [_Expression_] )? `;` -> -> _TraitType_ :\ ->    `type` [IDENTIFIER] ( `:` [_TypeParamBounds_]? )? `;` A _trait_ describes an abstract interface that types can implement. This interface consists of [associated items], which come in three varieties: @@ -61,10 +24,26 @@ other traits and so forth [as usual][generics]. Traits are implemented for specific types through separate [implementations]. -Items associated with a trait do not need to be defined in the trait, but they -may be. If the trait provides a definition, then this definition acts as a -default for any implementation which does not override it. If it does not, then -any implementation must provide a definition. +Trait functions may omit the function body by replacing it with a semicolon. +This indicates that the implementation must define the function. If the trait +function defines a body, this definition acts as a default for any +implementation which does not override it. Similarly, associated constants may +omit the equals sign and expression to indicate implementations must define +the constant value. Associated types must never define the type, the type may +only be specified in an implementation. + +```rust +// Examples of associated trait items with and without definitions. +trait Example { + const CONST_NO_DEFAULT: i32; + const CONST_WITH_DEFAULT: i32 = 99; + type TypeNoDefault; + fn method_without_default(&self); + fn method_with_default(&self) {} +} +``` + +Trait functions are are not allowed to be [`async`] or [`const`]. ## Trait bounds @@ -335,18 +314,10 @@ fn main() { [IDENTIFIER]: ../identifiers.md [WildcardPattern]: ../patterns.md#wildcard-pattern -[_BlockExpression_]: ../expressions/block-expr.md -[_Expression_]: ../expressions.md -[_FunctionQualifiers_]: functions.md -[_FunctionReturnType_]: functions.md -[_Generics_]: generics.md -[_MacroInvocationSemi_]: ../macros.md#macro-invocation -[_OuterAttribute_]: ../attributes.md +[_AssociatedItem_]: associated-items.md +[_GenericParams_]: generics.md [_InnerAttribute_]: ../attributes.md -[_Pattern_]: ../patterns.md -[_SelfParam_]: associated-items.md#methods [_TypeParamBounds_]: ../trait-bounds.md -[_Type_]: ../types.md#type-expressions [_Visibility_]: ../visibility-and-privacy.md [_WhereClause_]: generics.md#where-clauses [bounds]: ../trait-bounds.md @@ -366,3 +337,5 @@ fn main() { [`Box`]: ../special-types-and-traits.md#boxt [`Pin

`]: ../special-types-and-traits.md#pinp [`Rc`]: ../special-types-and-traits.md#rct +[`async`]: functions.md#async-functions +[`const`]: functions.md#const-functions diff --git a/src/doc/reference/src/items/type-aliases.md b/src/doc/reference/src/items/type-aliases.md index 6afd7a7fa7..c471f7a6a8 100644 --- a/src/doc/reference/src/items/type-aliases.md +++ b/src/doc/reference/src/items/type-aliases.md @@ -2,16 +2,14 @@ > **Syntax**\ > _TypeAlias_ :\ ->    `type` [IDENTIFIER] [_Generics_]? -> [_WhereClause_]? `=` [_Type_] `;` +>    `type` [IDENTIFIER] [_GenericParams_]? +> [_WhereClause_]? ( `=` [_Type_] ) `;` A _type alias_ defines a new name for an existing [type]. Type aliases are declared with the keyword `type`. Every value has a single, specific type, but may implement several different traits, or be compatible with several different type constraints. -[type]: ../types.md - For example, the following defines the type `Point` as a synonym for the type `(u8, u8)`, the type of pairs of unsigned 8 bit integers: @@ -32,7 +30,13 @@ let _ = UseAlias(5); // OK let _ = TypeAlias(5); // Doesn't work ``` +A type alias without the [_Type_] specification may only appear as an +[associated type] in a [trait]. + [IDENTIFIER]: ../identifiers.md -[_Generics_]: generics.md +[_GenericParams_]: generics.md [_WhereClause_]: generics.md#where-clauses [_Type_]: ../types.md#type-expressions +[associated type]: associated-items.md#associated-types +[trait]: traits.md +[type]: ../types.md diff --git a/src/doc/reference/src/items/unions.md b/src/doc/reference/src/items/unions.md index c974a9a553..dabb6355ca 100644 --- a/src/doc/reference/src/items/unions.md +++ b/src/doc/reference/src/items/unions.md @@ -2,7 +2,7 @@ > **Syntax**\ > _Union_ :\ ->    `union` [IDENTIFIER] [_Generics_]? [_WhereClause_]? +>    `union` [IDENTIFIER] [_GenericParams_]? [_WhereClause_]? > `{`[_StructFields_] `}` A union declaration uses the same syntax as a struct declaration, except with @@ -177,7 +177,7 @@ generics, trait implementations, inherent implementations, coherence, pattern checking, etc etc etc). [IDENTIFIER]: ../identifiers.md -[_Generics_]: generics.md +[_GenericParams_]: generics.md [_WhereClause_]: generics.md#where-clauses [_StructFields_]: structs.md [`transmute`]: ../../std/mem/fn.transmute.html diff --git a/src/doc/reference/src/items/use-declarations.md b/src/doc/reference/src/items/use-declarations.md index 8dc5848d82..b29ddcb183 100644 --- a/src/doc/reference/src/items/use-declarations.md +++ b/src/doc/reference/src/items/use-declarations.md @@ -202,5 +202,5 @@ m!(use std as _;); [IDENTIFIER]: ../identifiers.md [_SimplePath_]: ../paths.md#simple-paths [`extern crate`]: extern-crates.md -[extern prelude]: extern-crates.md#extern-prelude +[extern prelude]: ../names/preludes.md#extern-prelude [path qualifiers]: ../paths.md#path-qualifiers diff --git a/src/doc/reference/src/keywords.md b/src/doc/reference/src/keywords.md index 9df5b2a58e..e303b449e3 100644 --- a/src/doc/reference/src/keywords.md +++ b/src/doc/reference/src/keywords.md @@ -97,8 +97,8 @@ is possible to declare a variable or method with the name `union`. * `union` is used to declare a [union] and is only a keyword when used in a union declaration. -* `'static` is used for the static lifetime and cannot be used as a generic - lifetime parameter +* `'static` is used for the static lifetime and cannot be used as a [generic + lifetime parameter] or [loop label] ```compile_fail // error[E0262]: invalid lifetime parameter name: `'static` @@ -127,3 +127,5 @@ is possible to declare a variable or method with the name `union`. [union]: items/unions.md [variants]: items/enumerations.md [`dyn`]: types/trait-object.md +[loop label]: expressions/loop-expr.md#loop-labels +[generic lifetime parameter]: items/generics.md diff --git a/src/doc/reference/src/linkage.md b/src/doc/reference/src/linkage.md index 4a8ed96a96..2e91099e74 100644 --- a/src/doc/reference/src/linkage.md +++ b/src/doc/reference/src/linkage.md @@ -8,7 +8,7 @@ statically and dynamically. This section will explore the various methods to link crates together, and more information about native libraries can be found in the [FFI section of the book][ffi]. -[ffi]: ../book/ffi.html +[ffi]: ../book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code In one session of compilation, the compiler can generate multiple artifacts through the usage of either command line flags or the `crate_type` attribute. diff --git a/src/doc/reference/src/macros-by-example.md b/src/doc/reference/src/macros-by-example.md index 78c271403f..e97455d0b2 100644 --- a/src/doc/reference/src/macros-by-example.md +++ b/src/doc/reference/src/macros-by-example.md @@ -301,7 +301,7 @@ m!(); Second, it can be used to import macros from another crate, by attaching it to an `extern crate` declaration appearing in the crate's root module. Macros -imported this way are imported into the prelude of the crate, not textually, +imported this way are imported into the [`macro_use` prelude], not textually, which means that they can be shadowed by any other name. While macros imported by `#[macro_use]` can be used before the import statement, in case of a conflict, the last macro imported wins. Optionally, a list of macros to import @@ -497,3 +497,4 @@ For more detail, see the [formal specification]. [_Visibility_]: visibility-and-privacy.md [formal specification]: macro-ambiguity.md [token]: tokens.md +[`macro_use` prelude]: names/preludes.md#macro_use-prelude diff --git a/src/doc/reference/src/macros.md b/src/doc/reference/src/macros.md index f1ca1a9db6..65e43330d8 100644 --- a/src/doc/reference/src/macros.md +++ b/src/doc/reference/src/macros.md @@ -2,7 +2,7 @@ The functionality and syntax of Rust can be extended with custom definitions called macros. They are given names, and invoked through a consistent -syntax:`some_extension!(...)`. +syntax: `some_extension!(...)`. There are two ways to define new macros: diff --git a/src/doc/reference/src/names.md b/src/doc/reference/src/names.md new file mode 100644 index 0000000000..fd8f50cd02 --- /dev/null +++ b/src/doc/reference/src/names.md @@ -0,0 +1,143 @@ +# Names + +An *entity* is a language construct that can be referred to in some way within +the source program, usually via a [path]. Entities include [types], [items], +[generic parameters], [variable bindings], [loop labels], [lifetimes], +[fields], [attributes], and [lints]. + +A *declaration* is a syntactical construct that can introduce a *name* to +refer to an entity. Entity names are valid within a [*scope*] — a region of +source text where that name may be referenced. + +Some entities are [explicitly declared](#explicitly-declared-entities) in the +source code, and some are [implicitly declared](#implicitly-declared-entities) +as part of the language or compiler extensions. + +[*Paths*] are used to refer to an entity, possibly in another scope. Lifetimes +and loop labels use a [dedicated syntax][lifetimes-and-loop-labels] using a +leading quote. + +Names are segregated into different [*namespaces*], allowing entities in +different namespaces to share the same name without conflict. + +[*Name resolution*] is the compile-time process of tying paths, identifiers, +and labels to entity declarations. + +Access to certain names may be restricted based on their [*visibility*]. + +## Explicitly declared entities + +Entities that explicitly introduce a name in the source code are: + +* [Items]: + * [Module declarations] + * [External crate declarations] + * [Use declarations] + * [Function declarations] and [function parameters] + * [Type aliases] + * [struct], [union], [enum], enum variant declarations, and their named + fields + * [Constant item declarations] + * [Static item declarations] + * [Trait item declarations] and their [associated items] + * [External block items] + * [`macro_rules` declarations] and [matcher metavariables] + * [Implementation] associated items +* [Expressions]: + * [Closure] parameters + * [`while let`] pattern bindings + * [`for`] pattern bindings + * [`if let`] pattern bindings + * [`match`] pattern bindings + * [Loop labels] +* [Generic parameters] +* [Higher ranked trait bounds] +* [`let` statement] pattern bindings +* The [`macro_use` attribute] can introduce macro names from another crate +* The [`macro_export` attribute] can introduce an alias for the macro into the crate root + +Additionally, [macro invocations] and [attributes] can introduce names by +expanding to one of the above items. + +## Implicitly declared entities + +The following entities are implicitly defined by the language, or are +introduced by compiler options and extensions: + +* [Language prelude]: + * [Boolean type] — `bool` + * [Textual types] — `char` and `str` + * [Integer types] — `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128` + * [Machine-dependent integer types] — `usize` and `isize` + * [floating-point types] — `f32` and `f64` +* [Built-in attributes] +* [Standard library prelude] items, attributes, and macros +* [Standard library][extern-prelude] crates in the root module +* [External crates][extern-prelude] linked by the compiler +* [Tool attributes] +* [Lints] and [tool lint attributes] +* [Derive helper attributes] are valid within an item without being explicitly imported +* The [`'static`] lifetime + +Additionally, the crate root module does not have a name, but can be referred +to with certain [path qualifiers] or aliases. + + +[*Name resolution*]: names/name-resolution.md +[*namespaces*]: names/namespaces.md +[*paths*]: paths.md +[*scope*]: names/scopes.md +[*visibility*]: visibility-and-privacy.md +[`'static`]: keywords.md#weak-keywords +[`for`]: expressions/loop-expr.md#iterator-loops +[`if let`]: expressions/if-expr.md#if-let-expressions +[`let` statement]: statements.md#let-statements +[`macro_export` attribute]: macros-by-example.md#path-based-scope +[`macro_rules` declarations]: macros-by-example.md +[`macro_use` attribute]: macros-by-example.md#the-macro_use-attribute +[`match`]: expressions/match-expr.md +[`while let`]: expressions/loop-expr.md#predicate-pattern-loops +[associated items]: items/associated-items.md +[attributes]: attributes.md +[Boolean type]: types/boolean.md +[Built-in attributes]: attributes.md#built-in-attributes-index +[Closure]: expressions/closure-expr.md +[Constant item declarations]: items/constant-items.md +[Derive helper attributes]: procedural-macros.md#derive-macro-helper-attributes +[enum]: items/enumerations.md +[Expressions]: expressions.md +[extern-prelude]: names/preludes.md#extern-prelude +[External block items]: items/external-blocks.md +[External crate declarations]: items/extern-crates.md +[fields]: expressions/field-expr.md +[floating-point types]: types/numeric.md#floating-point-types +[Function declarations]: items/functions.md +[function parameters]: items/functions.md#function-parameters +[Generic parameters]: items/generics.md +[Higher ranked trait bounds]: trait-bounds.md#higher-ranked-trait-bounds +[Implementation]: items/implementations.md +[Integer types]: types/numeric.md#integer-types +[Items]: items.md +[Language prelude]: names/preludes.md#language-prelude +[lifetimes-and-loop-labels]: tokens.md#lifetimes-and-loop-labels +[lifetimes]: tokens.md#lifetimes-and-loop-labels +[Lints]: attributes/diagnostics.md#lint-check-attributes +[Loop labels]: expressions/loop-expr.md#loop-labels +[Machine-dependent integer types]: types/numeric.md#machine-dependent-integer-types +[macro invocations]: macros.md#macro-invocation +[matcher metavariables]: macros-by-example.md#metavariables +[Module declarations]: items/modules.md +[path]: paths.md +[path qualifiers]: paths.md#path-qualifiers +[Standard library prelude]: names/preludes.md#standard-library-prelude +[Static item declarations]: items/static-items.md +[struct]: items/structs.md +[Textual types]: types/textual.md +[Tool attributes]: attributes.md#tool-attributes +[tool lint attributes]: attributes/diagnostics.md#tool-lint-attributes +[Trait item declarations]: items/traits.md +[Type aliases]: items/type-aliases.md +[types]: types.md +[union]: items/unions.md +[Use declarations]: items/use-declarations.md +[variable bindings]: patterns.md diff --git a/src/doc/reference/src/names/name-resolution.md b/src/doc/reference/src/names/name-resolution.md new file mode 100644 index 0000000000..0f70697a62 --- /dev/null +++ b/src/doc/reference/src/names/name-resolution.md @@ -0,0 +1,3 @@ +# Name resolution + +> **Note**: This is a placeholder for future expansion. diff --git a/src/doc/reference/src/names/namespaces.md b/src/doc/reference/src/names/namespaces.md new file mode 100644 index 0000000000..8d2419b39f --- /dev/null +++ b/src/doc/reference/src/names/namespaces.md @@ -0,0 +1,158 @@ +# Namespaces + +A *namespace* is a logical grouping of declared [names]. Names are segregated +into separate namespaces based on the kind of entity the name refers to. +Namespaces allow the occurrence of a name in one namespace to not conflict +with the same name in another namespace. + +Within a namespace, names are organized in a hierarchy, where each level of +the hierarchy has its own collection of named entities. + +There are several different namespaces that each contain different kinds of +entities. The usage of a name will look for the declaration of that name in +different namespaces, based on the context, as described in the [name +resolution] chapter. + +The following is a list of namespaces, with their corresponding entities: + +* Type Namespace + * [Module declarations] + * [External crate declarations] + * [External crate prelude] items + * [Struct], [union], [enum], enum variant declarations + * [Trait item declarations] + * [Type aliases] + * [Associated type declarations] + * Built-in types: [boolean], [numeric], and [textual] + * [Generic type parameters] + * [`Self` type] + * [Tool attribute modules] +* Value Namespace + * [Function declarations] + * [Constant item declarations] + * [Static item declarations] + * [Struct constructors] + * [Enum variant constructors] + * [`Self` constructors] + * [Generic const parameters] + * [Associated const declarations] + * [Associated function declarations] + * Local bindings — [`let`], [`if let`], [`while let`], [`for`], [`match`] + arms, [function parameters], [closure parameters] + * Captured [closure] variables +* Macro Namespace + * [`macro_rules` declarations] + * [Built-in attributes] + * [Tool attributes] + * [Function-like procedural macros] + * [Derive macros] + * [Derive macro helpers] + * [Attribute macros] +* Lifetime Namespace + * [Generic lifetime parameters] +* Label Namespace[^rustc-lifetime-shadow] + * [Loop labels] + +An example of how overlapping names in different namespaces can be used unambiguously: + +```rust +// Foo introduces a type in the type namespace and a constructor in the value +// namespace. +struct Foo(u32); + +// The `Foo` macro is declared in the macro namespace. +macro_rules! Foo { + () => {}; +} + +// `Foo` in the `f` parameter type refers to `Foo` in the type namespace. +// `'Foo` introduces a new lifetime in the lifetime namespace. +fn example<'Foo>(f: Foo) { + // `Foo` refers to the `Foo` constructor in the value namespace. + let ctor = Foo; + // `Foo` refers to the `Foo` macro in the macro namespace. + Foo!{} + // `'Foo` introduces a label in the label namespace. + 'Foo: loop { + // `'Foo` refers to the `'Foo` lifetime parameter, and `Foo` + // refers to the type namespace. + let x: &'Foo Foo; + // `'Foo` refers to the label. + break 'Foo; + } +} +``` + +## Named entities without a namespace + +The following entities have explicit names, but the names are not a part of +any specific namespace. + +### Fields + +Even though struct, enum, and union fields are named, the named fields do not +live in an explicit namespace. They can only be accessed via a [field +expression], which only inspects the field names of the specific type being +accessed. + +### Use declarations + +A [use declaration] has named aliases that it imports into scope, but the +`use` item itself does not belong to a specific namespace. Instead, it can +introduce aliases into multiple namespaces, depending on the item kind being +imported. + + + +[^rustc-lifetime-shadow]: `rustc` currently warns about shadowing when using + the same name for a label and lifetime in the same scope, but it still + treats them independently. This is intended as a future-compatibility + warning about a possible extension to the language. See [PR + #24162](https://github.com/rust-lang/rust/pull/24162). + +[`for`]: ../expressions/loop-expr.md#iterator-loops +[`if let`]: ../expressions/if-expr.md#if-let-expressions +[`let`]: ../statements.md#let-statements +[`macro_rules` declarations]: ../macros-by-example.md +[`match`]: ../expressions/match-expr.md +[`Self` constructors]: ../paths.md#self-1 +[`Self` type]: ../paths.md#self-1 +[`while let`]: ../expressions/loop-expr.md#predicate-pattern-loops +[Associated const declarations]: ../items/associated-items.md#associated-constants +[Associated function declarations]: ../items/associated-items.md#associated-functions-and-methods +[Associated type declarations]: ../items/associated-items.md#associated-types +[Attribute macros]: ../procedural-macros.md#attribute-macros +[boolean]: ../types/boolean.md +[Built-in attributes]: ../attributes.md#built-in-attributes-index +[closure parameters]: ../expressions/closure-expr.md +[closure]: ../expressions/closure-expr.md +[Constant item declarations]: ../items/constant-items.md +[Derive macro helpers]: ../procedural-macros.md#derive-macro-helper-attributes +[Derive macros]: ../procedural-macros.md#derive-macros +[entity]: ../glossary.md#entity +[Enum variant constructors]: ../items/enumerations.md +[enum]: ../items/enumerations.md +[External crate declarations]: ../items/extern-crates.md +[External crate prelude]: preludes.md#extern-prelude +[field expression]: ../expressions/field-expr.md +[Function declarations]: ../items/functions.md +[function parameters]: ../items/functions.md#function-parameters +[Function-like procedural macros]: ../procedural-macros.md#function-like-procedural-macros +[Generic const parameters]: ../items/generics.md#const-generics +[Generic lifetime parameters]: ../items/generics.md +[Generic type parameters]: ../items/generics.md +[Loop labels]: ../expressions/loop-expr.md#loop-labels +[Module declarations]: ../items/modules.md +[name resolution]: name-resolution.md +[names]: ../names.md +[numeric]: ../types/numeric.md +[Static item declarations]: ../items/static-items.md +[Struct constructors]: ../items/structs.md +[Struct]: ../items/structs.md +[textual]: ../types/textual.md +[Tool attribute modules]: ../attributes.md#tool-attributes +[Tool attributes]: ../attributes.md#tool-attributes +[Trait item declarations]: ../items/traits.md +[Type aliases]: ../items/type-aliases.md +[union]: ../items/unions.md +[use declaration]: ../items/use-declarations.md diff --git a/src/doc/reference/src/names/preludes.md b/src/doc/reference/src/names/preludes.md new file mode 100644 index 0000000000..6027763157 --- /dev/null +++ b/src/doc/reference/src/names/preludes.md @@ -0,0 +1,157 @@ +# Preludes + +A *prelude* is a collection of names that are automatically brought into scope +of every module in a crate. + +These prelude names are not part of the module itself, they are implicitly +queried during [name resolution]. For example, even though something like +[`Box`] is in scope in every module, you cannot refer to it as `self::Box` +because it is not a member of the current module. + +There are several different preludes: + +- [Standard library prelude] +- [Extern prelude] +- [Language prelude] +- [`macro_use` prelude] +- [Tool prelude] + +## Standard library prelude + +The standard library prelude includes names from the [`std::prelude::v1`] +module. If the [`no_std` attribute] is used, then it instead uses the names +from the [`core::prelude::v1`] module. + +## Extern prelude + +External crates imported with [`extern crate`] in the root module or provided +to the compiler (as with the `--extern` flag with `rustc`) are added to the +*extern prelude*. If imported with an alias such as `extern crate orig_name as +new_name`, then the symbol `new_name` is instead added to the prelude. + +The [`core`] crate is always added to the extern prelude. The [`std`] crate is +added as long as the [`no_std` attribute] is not specified in the crate root. + +> **Edition Differences**: In the 2015 edition, crates in the extern prelude +> cannot be referenced via [use declarations], so it is generally standard +> practice to include `extern crate` declarations to bring them into scope. +> +> Beginning in the 2018 edition, [use declarations] can reference crates in +> the extern prelude, so it is considered unidiomatic to use `extern crate`. + +> **Note**: Additional crates that ship with `rustc`, such as [`alloc`], and +> [`test`], are not automatically included with the `--extern` flag when using +> Cargo. They must be brought into scope with an `extern crate` declaration, +> even in the 2018 edition. +> +> ```rust +> extern crate alloc; +> use alloc::rc::Rc; +> ``` +> +> Cargo does bring in `proc_macro` to the extern prelude for proc-macro crates +> only. + + + +### The `no_std` attribute + +By default, the standard library is automatically included in the crate root +module. The [`std`] crate is added to the root, along with an implicit +[`macro_use` attribute] pulling in all macros exported from `std` into the +[`macro_use` prelude]. Both [`core`] and [`std`] are added to the [extern +prelude]. The [standard library prelude] includes everything from the +[`std::prelude::v1`] module. + +The *`no_std` [attribute]* may be applied at the crate level to prevent the +[`std`] crate from being automatically added into scope. It does three things: + +* Prevents `std` from being added to the [extern prelude](#extern-prelude). +* Uses [`core::prelude::v1`] in the [standard library prelude] instead of + [`std::prelude::v1`]. +* Injects the [`core`] crate into the crate root instead of [`std`], and pulls + in all macros exported from `core` in the [`macro_use` prelude]. + +> **Note**: Using the core prelude over the standard prelude is useful when +> either the crate is targeting a platform that does not support the standard +> library or is purposefully not using the capabilities of the standard +> library. Those capabilities are mainly dynamic memory allocation (e.g. `Box` +> and `Vec`) and file and network capabilities (e.g. `std::fs` and `std::io`). + +

+ +Warning: Using `no_std` does not prevent the standard library from being +linked in. It is still valid to put `extern crate std;` into the crate and +dependencies can also link it in. + +
+ +## Language prelude + +The language prelude includes names of types and attributes that are built-in +to the language. The language prelude is always in scope. It includes the following: + +* [Type namespace] + * [Boolean type] — `bool` + * [Textual types] — `char` and `str` + * [Integer types] — `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128` + * [Machine-dependent integer types] — `usize` and `isize` + * [floating-point types] — `f32` and `f64` +* [Macro namespace] + * [Built-in attributes] + +## `macro_use` prelude + +The `macro_use` prelude includes macros from external crates that were +imported by the [`macro_use` attribute] applied to an [`extern crate`]. + +## Tool prelude + +The tool prelude includes tool names for external tools in the [type +namespace]. See the [tool attributes] section for more details. + +## The `no_implicit_prelude` attribute + +The *`no_implicit_prelude` [attribute]* may be applied at the crate level or +on a module to indicate that it should not automatically bring the [standard +library prelude], [extern prelude], or [tool prelude] into scope for that +module or any of its descendants. + +This attribute does not affect the [language prelude]. + +> **Edition Differences**: In the 2015 edition, the `no_implicit_prelude` +> attribute does not affect the [`macro_use` prelude], and all macros exported +> from the standard library are still included in the `macro_use` prelude. +> Starting in the 2018 edition, it will remove the `macro_use` prelude. + +[`alloc`]: ../../alloc/index.html +[`Box`]: ../../std/boxed/struct.Box.html +[`core::prelude::v1`]: ../../core/prelude/index.html +[`core`]: ../../core/index.html +[`extern crate`]: ../items/extern-crates.md +[`macro_use` attribute]: ../macros-by-example.md#the-macro_use-attribute +[`macro_use` prelude]: #macro_use-prelude +[`no_std` attribute]: #the-no_std-attribute +[`no_std` attribute]: #the-no_std-attribute +[`std::prelude::v1`]: ../../std/prelude/index.html +[`std`]: ../../std/index.html +[`test`]: ../../test/index.html +[attribute]: ../attributes.md +[Boolean type]: ../types/boolean.md +[Built-in attributes]: ../attributes.md#built-in-attributes-index +[extern prelude]: #extern-prelude +[floating-point types]: ../types/numeric.md#floating-point-types +[Integer types]: ../types/numeric.md#integer-types +[Language prelude]: #language-prelude +[Machine-dependent integer types]: ../types/numeric.md#machine-dependent-integer-types +[Macro namespace]: namespaces.md +[name resolution]: name-resolution.md +[Standard library prelude]: #standard-library-prelude +[Textual types]: ../types/textual.md +[tool attributes]: ../attributes.md#tool-attributes +[Tool prelude]: #tool-prelude +[Type namespace]: namespaces.md +[use declarations]: ../items/use-declarations.md diff --git a/src/doc/reference/src/names/scopes.md b/src/doc/reference/src/names/scopes.md new file mode 100644 index 0000000000..288781bd31 --- /dev/null +++ b/src/doc/reference/src/names/scopes.md @@ -0,0 +1,3 @@ +# Scopes + +> **Note**: This is a placeholder for future expansion. diff --git a/src/doc/reference/src/paths.md b/src/doc/reference/src/paths.md index 821d883d14..c0b2e6ff3e 100644 --- a/src/doc/reference/src/paths.md +++ b/src/doc/reference/src/paths.md @@ -50,22 +50,16 @@ mod m { > > _GenericArgs_ :\ >       `<` `>`\ ->    | `<` _GenericArgsLifetimes_ `,`? `>`\ ->    | `<` _GenericArgsTypes_ `,`? `>`\ ->    | `<` _GenericArgsBindings_ `,`? `>`\ ->    | `<` _GenericArgsTypes_ `,` _GenericArgsBindings_ `,`? `>`\ ->    | `<` _GenericArgsLifetimes_ `,` _GenericArgsTypes_ `,`? `>`\ ->    | `<` _GenericArgsLifetimes_ `,` _GenericArgsBindings_ `,`? `>`\ ->    | `<` _GenericArgsLifetimes_ `,` _GenericArgsTypes_ `,` _GenericArgsBindings_ `,`? `>` +>    | `<` ( _GenericArg_ `,` )\* _GenericArg_ `,`? `>` > -> _GenericArgsLifetimes_ :\ ->    [_Lifetime_] (`,` [_Lifetime_])\* +> _GenericArg_ :\ +>    [_Lifetime_] | [_Type_] | _GenericArgsConst_ | _GenericArgsBinding_ > -> _GenericArgsTypes_ :\ ->    [_Type_] (`,` [_Type_])\* -> -> _GenericArgsBindings_ :\ ->    _GenericArgsBinding_ (`,` _GenericArgsBinding_)\* +> _GenericArgsConst_ :\ +>       [_BlockExpression_]\ +>    | [_LiteralExpression_]\ +>    | `-` [_LiteralExpression_]\ +>    | [_SimplePathSegment_] > > _GenericArgsBinding_ :\ >    [IDENTIFIER] `=` [_Type_] @@ -81,6 +75,12 @@ ambiguity with the less-than operator. This is colloquially known as "turbofish" Vec::::with_capacity(1024); ``` +The order of generic arguments is restricted to lifetime arguments, then type +arguments, then const arguments, then equality constraints. + +Const arguments must be surrounded by braces unless they are a +[literal] or a single segment path. + ## Qualified paths > **Syntax**\ @@ -164,10 +164,11 @@ start being resolved from the crate root. Each identifier in the path must resol item. > **Edition Differences**: In the 2015 Edition, the crate root contains a variety of -> different items, including external crates, default crates such as `std` and `core`, and +> different items, including external crates, default crates such as `std` or `core`, and > items in the top level of the crate (including `use` imports). > -> Beginning with the 2018 Edition, paths starting with `::` can only reference crates. +> Beginning with the 2018 Edition, paths starting with `::` can only reference +> crates in the [extern prelude]. ```rust mod a { @@ -364,9 +365,14 @@ mod without { // ::without # fn main() {} ``` +[_BlockExpression_]: expressions/block-expr.md +[_Expression_]: expressions.md [_GenericArgs_]: #paths-in-expressions [_Lifetime_]: trait-bounds.md +[_LiteralExpression_]: expressions/literal-expr.md +[_SimplePathSegment_]: #simple-paths [_Type_]: types.md#type-expressions +[literal]: expressions/literal-expr.md [item]: items.md [variable]: variables.md [implementations]: items/implementations.md @@ -375,6 +381,7 @@ mod without { // ::without [`use`]: items/use-declarations.md [attributes]: attributes.md [expressions]: expressions.md +[extern prelude]: names/preludes.md#extern-prelude [macro transcribers]: macros-by-example.md [macros]: macros-by-example.md [patterns]: patterns.md diff --git a/src/doc/reference/src/tokens.md b/src/doc/reference/src/tokens.md index f329ce9124..15d8468a01 100644 --- a/src/doc/reference/src/tokens.md +++ b/src/doc/reference/src/tokens.md @@ -509,6 +509,7 @@ Examples of floating-point literals of various forms: 0.1f64; // type f64 0.1f32; // type f32 12E+99_f64; // type f64 +5f32; // type f32 let x: f64 = 2.; // type f64 ``` @@ -517,9 +518,7 @@ syntax with a floating point literal ending in a period. `2.f64` would attempt to call a method named `f64` on `2`. The representation semantics of floating-point numbers are described in -["Machine Types"]. - -["Machine Types"]: types/numeric.md +["Machine Types"][machine types]. ### Boolean literals @@ -637,6 +636,7 @@ them are referred to as "token trees" in [macros]. The three types of brackets [if let]: expressions/if-expr.md#if-let-expressions [keywords]: keywords.md [lazy-bool]: expressions/operator-expr.md#lazy-boolean-operators +[machine types]: types/numeric.md [macros]: macros-by-example.md [match]: expressions/match-expr.md [negation]: expressions/operator-expr.md#negation-operators diff --git a/src/doc/reference/src/types/enum.md b/src/doc/reference/src/types/enum.md index fca7851aaa..8f69dbad80 100644 --- a/src/doc/reference/src/types/enum.md +++ b/src/doc/reference/src/types/enum.md @@ -16,7 +16,7 @@ corresponding `enum` type, as well as the size needed to store a discriminant. Enum types cannot be denoted *structurally* as types, but must be denoted by named reference to an [`enum` item]. -[^enumtype]: ../The `enum` type is analogous to a `data` constructor declaration in +[^enumtype]: The `enum` type is analogous to a `data` constructor declaration in ML, or a *pick ADT* in Limbo. [`enum` item]: ../items/enumerations.md diff --git a/src/doc/reference/src/types/function-pointer.md b/src/doc/reference/src/types/function-pointer.md index 4a7a1ae47f..2059cf48bc 100644 --- a/src/doc/reference/src/types/function-pointer.md +++ b/src/doc/reference/src/types/function-pointer.md @@ -2,9 +2,12 @@ > **Syntax**\ > _BareFunctionType_ :\ ->    [_ForLifetimes_]? [_FunctionQualifiers_] `fn`\ +>    [_ForLifetimes_]? _FunctionTypeQualifiers_ `fn`\ >       `(` _FunctionParametersMaybeNamedVariadic_? `)` _BareFunctionReturnType_? > +> _FunctionTypeQualifiers_:\ +>    `unsafe`? (`extern` [_Abi_]?)? +> > _BareFunctionReturnType_:\ >    `->` [_TypeNoBounds_] > @@ -50,8 +53,8 @@ Attributes on function pointer parameters follow the same rules and restrictions as [regular function parameters]. [IDENTIFIER]: ../identifiers.md +[_Abi_]: ../items/functions.md [_ForLifetimes_]: ../items/generics.md#where-clauses -[_FunctionQualifiers_]: ../items/functions.md [_TypeNoBounds_]: ../types.md#type-expressions [_Type_]: ../types.md#type-expressions [_OuterAttribute_]: ../attributes.md diff --git a/src/doc/reference/src/types/struct.md b/src/doc/reference/src/types/struct.md index 40ddb73ace..1f20dbb3c6 100644 --- a/src/doc/reference/src/types/struct.md +++ b/src/doc/reference/src/types/struct.md @@ -21,7 +21,7 @@ A _unit-like struct_ type is like a struct type, except that it has no fields. The one value constructed by the associated [struct expression] is the only value that inhabits such a type. -[^structtype]: ../`struct` types are analogous to `struct` types in C, the +[^structtype]: `struct` types are analogous to `struct` types in C, the *record* types of the ML family, or the *struct* types of the Lisp family. [`repr` attribute]: ../type-layout.md#representations diff --git a/src/doc/reference/src/types/tuple.md b/src/doc/reference/src/types/tuple.md index 3ee3e76ea2..bfef430531 100644 --- a/src/doc/reference/src/types/tuple.md +++ b/src/doc/reference/src/types/tuple.md @@ -36,6 +36,7 @@ index expression] or [pattern matching]. [^1]: Structural types are always equivalent if their internal types are equivalent. For a nominal version of tuples, see [tuple structs]. + [^2]: Element is equivalent to field, except numerical indexes instead of identifiers diff --git a/src/doc/rust-by-example/.travis.yml b/src/doc/rust-by-example/.travis.yml index 8c406cea16..17adda5b9c 100644 --- a/src/doc/rust-by-example/.travis.yml +++ b/src/doc/rust-by-example/.travis.yml @@ -10,7 +10,7 @@ before_script: set -ex rustup --version rustc -Vv - curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.3.7/mdbook-v0.3.7-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=$HOME/.cargo/bin + curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.5/mdbook-v0.4.5-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=$HOME/.cargo/bin mdbook --version rustup toolchain update nightly -c rust-docs script: diff --git a/src/doc/rust-by-example/src/flow_control/match/guard.md b/src/doc/rust-by-example/src/flow_control/match/guard.md index 336b7f1233..9caa65aa00 100644 --- a/src/doc/rust-by-example/src/flow_control/match/guard.md +++ b/src/doc/rust-by-example/src/flow_control/match/guard.md @@ -18,6 +18,22 @@ fn main() { } ``` +Note that the compiler does not check arbitrary expressions for whether all +possible conditions have been checked. Therefore, you must use the `_` pattern +at the end. + +```rust,editable +fn main() { + let number: u8 = 4; + + match number { + i if i == 0 => println!("Zero"), + i if i > 0 => println!("Greater than zero"), + _ => println!("Fell through"), // This should not be possible to reach + } +} +``` + ### See also: [Tuples](../../primitives/tuples.md) diff --git a/src/doc/rust-by-example/src/fn/closures.md b/src/doc/rust-by-example/src/fn/closures.md index baf1c422c1..68bfd8adf6 100644 --- a/src/doc/rust-by-example/src/fn/closures.md +++ b/src/doc/rust-by-example/src/fn/closures.md @@ -7,9 +7,9 @@ example, a closure that captures the x variable: |val| val + x ``` -The syntax and capabilities of closures make them very convenient for +The syntax and capabilities of closures make them very convenient for on the fly usage. Calling a closure is exactly like calling a function. -However, both input and return types *can* be inferred and input +However, both input and return types *can* be inferred and input variable names *must* be specified. Other characteristics of closures include: @@ -20,7 +20,7 @@ Other characteristics of closures include: ```rust,editable fn main() { // Increment via closures and functions. - fn function (i: i32) -> i32 { i + 1 } + fn function(i: i32) -> i32 { i + 1 } // Closures are anonymous, here we are binding them to references // Annotation is identical to function annotation but is optional diff --git a/src/doc/rust-by-example/src/std_misc/threads/testcase_mapreduce.md b/src/doc/rust-by-example/src/std_misc/threads/testcase_mapreduce.md index 9efc34a87f..641dc227cd 100644 --- a/src/doc/rust-by-example/src/std_misc/threads/testcase_mapreduce.md +++ b/src/doc/rust-by-example/src/std_misc/threads/testcase_mapreduce.md @@ -103,21 +103,13 @@ fn main() { * Collect our intermediate results, and combine them into a final result ************************************************************************/ - // collect each thread's intermediate results into a new Vec - let mut intermediate_sums = vec![]; - for child in children { - // collect each child thread's return-value - let intermediate_sum = child.join().unwrap(); - intermediate_sums.push(intermediate_sum); - } - - // combine all intermediate sums into a single final sum. + // combine each thread's intermediate results into a single final sum. // // we use the "turbofish" ::<> to provide sum() with a type hint. // // TODO: try without the turbofish, by instead explicitly // specifying the type of final_result - let final_result = intermediate_sums.iter().sum::(); + let final_result = children.into_iter().map(|c| c.join().unwrap()).sum::(); println!("Final sum result: {}", final_result); } diff --git a/src/doc/rustc/book.toml b/src/doc/rustc/book.toml index 8adc05c513..21d127c39c 100644 --- a/src/doc/rustc/book.toml +++ b/src/doc/rustc/book.toml @@ -3,3 +3,6 @@ authors = ["The Rust Project Developers"] multilingual = false src = "src" title = "The rustc book" + +[output.html] +git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustc" diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index f6493e49c3..51e7d987d9 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -492,6 +492,34 @@ point instructions in software. It takes one of the following values: * `y`, `yes`, `on`, or no value: use soft floats. * `n`, `no`, or `off`: use hardware floats (the default). +## split-debuginfo + +This option controls the emission of "split debuginfo" for debug information +that `rustc` generates. The default behavior of this option is +platform-specific, and not all possible values for this option work on all +platform. Possible values are: + +* `off` - This is the default for platforms with ELF binaries and windows-gnu + (not Windows MSVC and not macOS). This typically means that dwarf debug + information can be found in the final artifact in sections of the executable. + This option is not supported on Windows MSVC. On macOS this options prevents + the final execution of `dsymutil` to generate debuginfo. + +* `packed` - This is the default for Windows MSVC and macOS platforms. The term + "packed" here means that all the debug information is packed into a separate + file from the main executable. On Windows MSVC this is a `*.pdb` file, on + macOS this is a `*.dSYM` folder, and on other platforms this is a `*.dwp` + files. + +* `unpacked` - This means that debug information will be found in separate + files for each compilation unit (object file). This is not supported on + Windows MSVC. On macOS this means the original object files will contain + debug information. On other Unix platforms this means that `*.dwo` files will + contain debug information. + +Note that `packed` and `unpacked` gated behind `-Zunstable-options` on +non-macOS platforms at this time. + ## target-cpu This instructs `rustc` to generate code specifically for a particular processor. @@ -499,7 +527,7 @@ This instructs `rustc` to generate code specifically for a particular processor. You can run `rustc --print target-cpus` to see the valid options to pass here. Each target has a default base CPU. Special values include: -* `native` can be passed to use the processor of the host machine. +* `native` can be passed to use the processor of the host machine. * `generic` refers to an LLVM target with minimal features but modern tuning. ## target-feature diff --git a/src/doc/rustc/src/lints/listing/deny-by-default.md b/src/doc/rustc/src/lints/listing/deny-by-default.md new file mode 100644 index 0000000000..3c1452d646 --- /dev/null +++ b/src/doc/rustc/src/lints/listing/deny-by-default.md @@ -0,0 +1,3 @@ +# Deny-by-default lints + +This file is auto-generated by the lint-docs script. diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index ce8caae375..eb74041964 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -156,11 +156,14 @@ target | std | host | notes `aarch64-apple-tvos` | * | | ARM64 tvOS `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD `aarch64-unknown-hermit` | ? | | +`aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) `aarch64-unknown-netbsd` | ✓ | ✓ | `aarch64-unknown-openbsd` | ✓ | ✓ | ARM64 OpenBSD `aarch64-unknown-redox` | ? | | ARM64 Redox OS `aarch64-uwp-windows-msvc` | ? | | `aarch64-wrs-vxworks` | ? | | +`aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian) +`aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI) `armv4t-unknown-linux-gnueabi` | ? | | `armv5te-unknown-linux-uclibceabi` | ? | | ARMv5TE Linux with uClibc `armv6-unknown-freebsd` | ✓ | ✓ | ARMv6 FreeBSD diff --git a/src/doc/rustdoc/book.toml b/src/doc/rustdoc/book.toml index ba30c10766..c2e7ff5890 100644 --- a/src/doc/rustdoc/book.toml +++ b/src/doc/rustdoc/book.toml @@ -2,3 +2,6 @@ authors = ["The Rust Project Developers"] src = "src" title = "The rustdoc book" + +[output.html] +git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustdoc" diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index 31e002810c..80f7851deb 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -237,6 +237,26 @@ for a target triple that's different than your host triple. All of the usual caveats of cross-compiling code apply. +## `--default-theme`: set the default theme + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs --default-theme=ayu +``` + +Sets the default theme (for users whose browser has not remembered a +previous theme selection from the on-page theme picker). + +The supplied value should be the lowercase version of the theme name. +The set of available themes can be seen in the theme picker in the +generated output. + +Note that the set of available themes - and their appearance - is not +necessarily stable from one rustdoc version to the next. If the +requested theme does not exist, the builtin default (currently +`light`) is used instead. + ## `--markdown-css`: include more CSS files when rendering markdown Using this flag looks like this: diff --git a/src/doc/rustdoc/src/how-to-write-documentation.md b/src/doc/rustdoc/src/how-to-write-documentation.md index ca6db26da3..41736e5ee3 100644 --- a/src/doc/rustdoc/src/how-to-write-documentation.md +++ b/src/doc/rustdoc/src/how-to-write-documentation.md @@ -153,11 +153,73 @@ and finally provides a code example. ## Markdown -`rustdoc` uses the [commonmark markdown specification]. You might be +`rustdoc` uses the [CommonMark markdown specification]. You might be interested into taking a look at their website to see what's possible to do. - [commonmark quick reference] - [current spec] +In addition to the standard CommonMark syntax, `rustdoc` supports several +extensions: + +### Strikethrough + +Text may be rendered with a horizontal line through the center by wrapping the +text with two tilde characters on each side: + +```text +An example of ~~strikethrough text~~. +``` + +This example will render as: + +> An example of ~~strikethrough text~~. + +This follows the [GitHub Strikethrough extension][strikethrough]. + +### Footnotes + +A footnote generates a small numbered link in the text which when clicked +takes the reader to the footnote text at the bottom of the item. The footnote +label is written similarly to a link reference with a caret at the front. The +footnote text is written like a link reference definition, with the text +following the label. Example: + +```text +This is an example of a footnote[^note]. + +[^note]: This text is the contents of the footnote, which will be rendered + towards the bottom. +``` + +This example will render as: + +> This is an example of a footnote[^note]. +> +> [^note]: This text is the contents of the footnote, which will be rendered +> towards the bottom. + +The footnotes are automatically numbered based on the order the footnotes are +written. + +### Tables + +Tables can be written using pipes and dashes to draw the rows and columns of +the table. These will be translated to HTML table matching the shape. Example: + +```text +| Header1 | Header2 | +|---------|---------| +| abc | def | +``` + +This example will render similarly to this: + +> | Header1 | Header2 | +> |---------|---------| +> | abc | def | + +See the specification for the [GitHub Tables extension][tables] for more +details on the exact syntax supported. [`backtrace`]: https://docs.rs/backtrace/0.3.50/backtrace/ [commonmark markdown specification]: https://commonmark.org/ @@ -170,3 +232,5 @@ interested into taking a look at their website to see what's possible to do. [standard library]: https://doc.rust-lang.org/stable/std/index.html [current spec]: https://spec.commonmark.org/current/ [`std::env`]: https://doc.rust-lang.org/stable/std/env/index.html#functions +[strikethrough]: https://github.github.com/gfm/#strikethrough-extension- +[tables]: https://github.github.com/gfm/#tables-extension- diff --git a/src/doc/rustdoc/src/what-is-rustdoc.md b/src/doc/rustdoc/src/what-is-rustdoc.md index 1f6dced180..32dc1e02bb 100644 --- a/src/doc/rustdoc/src/what-is-rustdoc.md +++ b/src/doc/rustdoc/src/what-is-rustdoc.md @@ -10,7 +10,7 @@ CSS, and JavaScript. Let's give it a try! Create a new project with Cargo: ```bash -$ cargo new docs +$ cargo new docs --lib $ cd docs ``` diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 93908e9190..d03d5c7501 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -31,7 +31,12 @@ with runtime flag `ASAN_OPTIONS=detect_leaks=1` on macOS. AddressSanitizer is supported on the following targets: +* `aarch64-apple-darwin` +* `aarch64-fuchsia` +* `aarch64-unknown-linux-gnu` * `x86_64-apple-darwin` +* `x86_64-fuchsia` +* `x86_64-unknown-freebsd` * `x86_64-unknown-linux-gnu` AddressSanitizer works with non-instrumented code although it will impede its @@ -169,10 +174,26 @@ Shadow byte legend (one shadow byte represents 8 application bytes): ==39249==ABORTING ``` +# LeakSanitizer + +LeakSanitizer is run-time memory leak detector. + +LeakSanitizer is supported on the following targets: + +* `aarch64-apple-darwin` +* `aarch64-unknown-linux-gnu` +* `x86_64-apple-darwin` +* `x86_64-unknown-linux-gnu` + # MemorySanitizer -MemorySanitizer is detector of uninitialized reads. It is only supported on the -`x86_64-unknown-linux-gnu` target. +MemorySanitizer is detector of uninitialized reads. + +MemorySanitizer is supported on the following targets: + +* `aarch64-unknown-linux-gnu` +* `x86_64-unknown-freebsd` +* `x86_64-unknown-linux-gnu` MemorySanitizer requires all program code to be instrumented. C/C++ dependencies need to be recompiled using Clang with `-fsanitize=memory` option. Failing to @@ -219,7 +240,10 @@ $ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu ThreadSanitizer is a data race detection tool. It is supported on the following targets: +* `aarch64-apple-darwin` +* `aarch64-unknown-linux-gnu` * `x86_64-apple-darwin` +* `x86_64-unknown-freebsd` * `x86_64-unknown-linux-gnu` To work correctly ThreadSanitizer needs to be "aware" of all synchronization diff --git a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md index 98bcadd12e..8aca005214 100644 --- a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md +++ b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md @@ -123,7 +123,7 @@ The `rustup` option is guaranteed to install a compatible version of the LLVM to ```shell $ rustup component add llvm-tools-preview $ cargo install cargo-binutils -$ cargo profdata -- --help # note the additional "--" preceeding the tool-specific arguments +$ cargo profdata -- --help # note the additional "--" preceding the tool-specific arguments ``` ## Creating coverage reports diff --git a/src/doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md b/src/doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md new file mode 100644 index 0000000000..79a177cb28 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md @@ -0,0 +1,88 @@ +# `abi_c_cmse_nonsecure_call` + +The tracking issue for this feature is: [#81391] + +[#81391]: https://github.com/rust-lang/rust/issues/81391 + +------------------------ + +The [TrustZone-M +feature](https://developer.arm.com/documentation/100690/latest/) is available +for targets with the Armv8-M architecture profile (`thumbv8m` in their target +name). +LLVM, the Rust compiler and the linker are providing +[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the +TrustZone-M feature. + +One of the things provided, with this unstable feature, is the +`C-cmse-nonsecure-call` function ABI. This ABI is used on function pointers to +non-secure code to mark a non-secure function call (see [section +5.5](https://developer.arm.com/documentation/ecm0359818/latest/) for details). + +With this ABI, the compiler will do the following to perform the call: +* save registers needed after the call to Secure memory +* clear all registers that might contain confidential information +* clear the Least Significant Bit of the function address +* branches using the BLXNS instruction + +To avoid using the non-secure stack, the compiler will constrain the number and +type of parameters/return value. + +The `extern "C-cmse-nonsecure-call"` ABI is otherwise equivalent to the +`extern "C"` ABI. + + + +``` rust,ignore +#![no_std] +#![feature(abi_c_cmse_nonsecure_call)] + +#[no_mangle] +pub fn call_nonsecure_function(addr: usize) -> u32 { + let non_secure_function = + unsafe { core::mem::transmute:: u32>(addr) }; + non_secure_function() +} +``` + +``` text +$ rustc --emit asm --crate-type lib --target thumbv8m.main-none-eabi function.rs + +call_nonsecure_function: + .fnstart + .save {r7, lr} + push {r7, lr} + .setfp r7, sp + mov r7, sp + .pad #16 + sub sp, #16 + str r0, [sp, #12] + ldr r0, [sp, #12] + str r0, [sp, #8] + b .LBB0_1 +.LBB0_1: + ldr r0, [sp, #8] + push.w {r4, r5, r6, r7, r8, r9, r10, r11} + bic r0, r0, #1 + mov r1, r0 + mov r2, r0 + mov r3, r0 + mov r4, r0 + mov r5, r0 + mov r6, r0 + mov r7, r0 + mov r8, r0 + mov r9, r0 + mov r10, r0 + mov r11, r0 + mov r12, r0 + msr apsr_nzcvq, r0 + blxns r0 + pop.w {r4, r5, r6, r7, r8, r9, r10, r11} + str r0, [sp, #4] + b .LBB0_2 +.LBB0_2: + ldr r0, [sp, #4] + add sp, #16 + pop {r7, pc} +``` diff --git a/src/doc/unstable-book/src/language-features/const-in-array-repeat-expressions.md b/src/doc/unstable-book/src/language-features/const-in-array-repeat-expressions.md deleted file mode 100644 index 940916944b..0000000000 --- a/src/doc/unstable-book/src/language-features/const-in-array-repeat-expressions.md +++ /dev/null @@ -1,11 +0,0 @@ -# `const_in_array_repeat_expressions` - -The tracking issue for this feature is: [#49147] - -[#49147]: https://github.com/rust-lang/rust/issues/49147 - ------------------------- - -Relaxes the rules for repeat expressions, `[x; N]` such that `x` may also be `const` (strictly -speaking rvalue promotable), in addition to `typeof(x): Copy`. The result of `[x; N]` where `x` is -`const` is itself also `const`. diff --git a/src/doc/unstable-book/src/language-features/ffi-pure.md b/src/doc/unstable-book/src/language-features/ffi-pure.md index 4aef4eeab5..236ccb9f90 100644 --- a/src/doc/unstable-book/src/language-features/ffi-pure.md +++ b/src/doc/unstable-book/src/language-features/ffi-pure.md @@ -31,7 +31,7 @@ parameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not referentially-transparent, and are therefore more relaxed than `#[ffi_const]` functions. -However, accesing global memory through volatile or atomic reads can violate the +However, accessing global memory through volatile or atomic reads can violate the requirement that two consecutive function calls shall return the same value. A `pure` function that returns unit has no effect on the abstract machine's diff --git a/src/doc/unstable-book/src/language-features/intra-doc-pointers.md b/src/doc/unstable-book/src/language-features/intra-doc-pointers.md new file mode 100644 index 0000000000..fbc83f4b4f --- /dev/null +++ b/src/doc/unstable-book/src/language-features/intra-doc-pointers.md @@ -0,0 +1,15 @@ +# `intra-doc-pointers` + +The tracking issue for this feature is: [#80896] + +[#80896]: https://github.com/rust-lang/rust/issues/80896 + +------------------------ + +Rustdoc does not currently allow disambiguating between `*const` and `*mut`, and +raw pointers in intra-doc links are unstable until it does. + +```rust +#![feature(intra_doc_pointers)] +//! [pointer::add] +``` diff --git a/src/doc/unstable-book/src/language-features/link-args.md b/src/doc/unstable-book/src/language-features/link-args.md index 2507197661..da36e15800 100644 --- a/src/doc/unstable-book/src/language-features/link-args.md +++ b/src/doc/unstable-book/src/language-features/link-args.md @@ -15,7 +15,7 @@ usage would be: #![feature(link_args)] #[link_args = "-foo -bar -baz"] -extern {} +extern "C" {} # fn main() {} ``` diff --git a/src/test/rustdoc-json/check_missing_items.py b/src/etc/check_missing_items.py similarity index 99% rename from src/test/rustdoc-json/check_missing_items.py rename to src/etc/check_missing_items.py index 3a3bf7fa3e..c7ca0134f9 100644 --- a/src/test/rustdoc-json/check_missing_items.py +++ b/src/etc/check_missing_items.py @@ -4,6 +4,8 @@ # `index` or `paths`. It DOES NOT check that the structure of the produced json is actually in # any way correct, for example an empty map would pass. +# FIXME: Better error output + import sys import json diff --git a/src/etc/generate-deriving-span-tests.py b/src/etc/generate-deriving-span-tests.py index a0ba47e1db..d38f5add74 100755 --- a/src/etc/generate-deriving-span-tests.py +++ b/src/etc/generate-deriving-span-tests.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ -This script creates a pile of compile-fail tests check that all the +This script creates a pile of UI tests check that all the derives have spans that point to the fields, rather than the #[derive(...)] line. diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py index 440181a761..2f7233685d 100644 --- a/src/etc/htmldocck.py +++ b/src/etc/htmldocck.py @@ -218,7 +218,7 @@ def concat_multi_lines(f): LINE_PATTERN = re.compile(r''' - (?<=(?!?) + (?<=(?!?)@(?P!?) (?P[A-Za-z]+(?:-[A-Za-z]+)*) (?P.*)$ ''', re.X | re.UNICODE) @@ -233,6 +233,16 @@ def get_commands(template): negated = (m.group('negated') == '!') cmd = m.group('cmd') + if m.group('invalid') == '!': + print_err( + lineno, + line, + 'Invalid command: `!@{0}{1}`, (help: try with `@!{1}`)'.format( + '!' if negated else '', + cmd, + ), + ) + continue args = m.group('args') if args and not args[:1].isspace(): print_err(lineno, line, 'Invalid template syntax') diff --git a/src/etc/natvis/intrinsic.natvis b/src/etc/natvis/intrinsic.natvis index 874550da8b..030892a432 100644 --- a/src/etc/natvis/intrinsic.natvis +++ b/src/etc/natvis/intrinsic.natvis @@ -4,17 +4,21 @@ {data_ptr,[length]s8} data_ptr,[length]s8 - length - - length - data_ptr - + length + + + + length + data_ptr + + + - {{ length={length} }} + {{ len={length} }} - length + length length data_ptr diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis index de30b58526..cfaafc5734 100644 --- a/src/etc/natvis/liballoc.natvis +++ b/src/etc/natvis/liballoc.natvis @@ -1,9 +1,9 @@ - {{ size={len} }} + {{ len={len} }} - len + len buf.cap len @@ -12,9 +12,9 @@ - {{ size={tail <= head ? head - tail : buf.cap - tail + head} }} + {{ len={tail <= head ? head - tail : buf.cap - tail + head} }} - tail <= head ? head - tail : buf.cap - tail + head + tail <= head ? head - tail : buf.cap - tail + head buf.cap @@ -31,7 +31,7 @@ - {{ size={len} }} + {{ len={len} }} len @@ -42,15 +42,37 @@ - {*(char**)this,[vec.len]s8} - *(char**)this,[vec.len]s8 + {(char*)vec.buf.ptr.pointer,[vec.len]s8} + (char*)vec.buf.ptr.pointer,[vec.len]s8 - vec.len + vec.len vec.buf.cap - - vec.len - *(char**)this - + + + + vec.len + (char*)vec.buf.ptr.pointer + + + + + + + {ptr.pointer->value} + + ptr.pointer->value + + + + {ptr.pointer->data} + + ptr.pointer->data + + + + {ptr.pointer->data} + + ptr.pointer->data diff --git a/src/etc/natvis/libcore.natvis b/src/etc/natvis/libcore.natvis index 0e703b3b95..984a8bfb13 100644 --- a/src/etc/natvis/libcore.natvis +++ b/src/etc/natvis/libcore.natvis @@ -6,34 +6,28 @@ pointer + {{ Shared {pointer} }} pointer + - {{ None }} - {{ Some {__0} }} + None + Some({__0}) - (ULONG)(RUST$ENUM$DISR != 0) - __0 - - (ULONG)(RUST$ENUM$DISR != 0) - &__0 - + __0 + - {{ None }} - {{ Some {($T1 *)this} }} + None + Some({($T1 *)this}) - (ULONG)(*(PVOID *)this != nullptr) - ($T1 *)this - - (ULONG)(*(PVOID *)this != nullptr) - ($T1 *)this - + ($T1 *)this + \ No newline at end of file diff --git a/src/etc/natvis/libstd.natvis b/src/etc/natvis/libstd.natvis index 9550c25f2f..7e5ee7b13d 100644 --- a/src/etc/natvis/libstd.natvis +++ b/src/etc/natvis/libstd.natvis @@ -26,9 +26,9 @@ --> - {{ size={base.table.items} }} + {{ len={base.table.items} }} - base.table.items + base.table.items base.table.items + base.table.growth_left base.hash_builder @@ -50,9 +50,9 @@ - {{ size={base.map.table.items} }} + {{ len={base.map.table.items} }} - base.map.table.items + base.map.table.items base.map.table.items + base.map.table.growth_left base.map.hash_builder diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index b0f5bac6ab..b6965898b4 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" path = "lib.rs" [dependencies] +arrayvec = { version = "0.5.1", default-features = false } pulldown-cmark = { version = "0.8", default-features = false } minifier = "0.0.33" rayon = { version = "0.3.0", package = "rustc-rayon" } @@ -17,6 +18,7 @@ smallvec = "1.0" tempfile = "3" itertools = "0.9" regex = "1" +rustdoc-json-types = { path = "../rustdoc-json-types" } [dev-dependencies] expect-test = "1.0" diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 96d19e8e17..83114a72b8 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -84,14 +84,14 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { new_generics }); - let polarity; + let negative_polarity; let new_generics = match result { AutoTraitResult::PositiveImpl(new_generics) => { - polarity = None; + negative_polarity = false; new_generics } AutoTraitResult::NegativeImpl => { - polarity = Some(ImplPolarity::Negative); + negative_polarity = true; // For negative impls, we use the generic params, but *not* the predicates, // from the original type. Otherwise, the displayed impl appears to be a @@ -123,14 +123,14 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { attrs: Default::default(), visibility: Inherited, def_id: self.cx.next_def_id(param_env_def_id.krate), - kind: ImplItem(Impl { + kind: box ImplItem(Impl { unsafety: hir::Unsafety::Normal, generics: new_generics, provided_trait_methods: Default::default(), trait_: Some(trait_ref.clean(self.cx).get_trait_type().unwrap()), for_: ty.clean(self.cx), items: Vec::new(), - polarity, + negative_polarity, synthetic: true, blanket_impl: None, }), @@ -313,12 +313,12 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { tcx: TyCtxt<'tcx>, pred: ty::Predicate<'tcx>, ) -> FxHashSet { - let bound_predicate = pred.bound_atom(); + let bound_predicate = pred.kind(); let regions = match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(poly_trait_pred, _) => { + ty::PredicateKind::Trait(poly_trait_pred, _) => { tcx.collect_referenced_late_bound_regions(&bound_predicate.rebind(poly_trait_pred)) } - ty::PredicateAtom::Projection(poly_proj_pred) => { + ty::PredicateKind::Projection(poly_proj_pred) => { tcx.collect_referenced_late_bound_regions(&bound_predicate.rebind(poly_proj_pred)) } _ => return FxHashSet::default(), @@ -351,8 +351,8 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { if let Some(data) = ty_to_fn.get(&ty) { let (poly_trait, output) = (data.0.as_ref().expect("as_ref failed").clone(), data.1.as_ref().cloned()); - let new_ty = match &poly_trait.trait_ { - &Type::ResolvedPath { + let new_ty = match poly_trait.trait_ { + Type::ResolvedPath { ref path, ref param_names, ref did, @@ -463,8 +463,8 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { .iter() .filter(|p| { !orig_bounds.contains(p) - || match p.skip_binders() { - ty::PredicateAtom::Trait(pred, _) => pred.def_id() == sized_trait, + || match p.kind().skip_binder() { + ty::PredicateKind::Trait(pred, _) => pred.def_id() == sized_trait, _ => false, } }) @@ -597,7 +597,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { ref mut bindings, .. } => { bindings.push(TypeBinding { - name: left_name.clone(), + name: left_name, kind: TypeBindingKind::Equality { ty: rhs }, }); } @@ -665,7 +665,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { GenericParamDefKind::Type { ref mut default, ref mut bounds, .. } => { // We never want something like `impl`. default.take(); - let generic_ty = Type::Generic(param.name.clone()); + let generic_ty = Type::Generic(param.name); if !has_sized.contains(&generic_ty) { bounds.insert(0, GenericBound::maybe_sized(self.cx)); } @@ -738,11 +738,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { } fn is_fn_ty(&self, tcx: TyCtxt<'_>, ty: &Type) -> bool { - match &ty { - &&Type::ResolvedPath { ref did, .. } => { - *did == tcx.require_lang_item(LangItem::Fn, None) - || *did == tcx.require_lang_item(LangItem::FnMut, None) - || *did == tcx.require_lang_item(LangItem::FnOnce, None) + match ty { + &Type::ResolvedPath { did, .. } => { + did == tcx.require_lang_item(LangItem::Fn, None) + || did == tcx.require_lang_item(LangItem::FnMut, None) + || did == tcx.require_lang_item(LangItem::FnOnce, None) } _ => false, } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index d99055e114..f1c26feea4 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -112,7 +112,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { attrs: Default::default(), visibility: Inherited, def_id: self.cx.next_def_id(impl_def_id.krate), - kind: ImplItem(Impl { + kind: box ImplItem(Impl { unsafety: hir::Unsafety::Normal, generics: ( self.cx.tcx.generics_of(impl_def_id), @@ -131,7 +131,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { .in_definition_order() .collect::>() .clean(self.cx), - polarity: None, + negative_polarity: false, synthetic: false, blanket_impl: Some(trait_ref.self_ty().clean(self.cx)), }), diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 2f169d1d3f..444b73246d 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -177,10 +177,7 @@ impl Cfg { Cfg::Any(ref sub_cfgs) | Cfg::All(ref sub_cfgs) => { sub_cfgs.first().map(Cfg::should_capitalize_first_letter).unwrap_or(false) } - Cfg::Cfg(name, _) => match name { - sym::debug_assertions | sym::target_endian => true, - _ => false, - }, + Cfg::Cfg(name, _) => name == sym::debug_assertions || name == sym::target_endian, } } @@ -188,18 +185,13 @@ impl Cfg { match *self { Cfg::False | Cfg::True => false, Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true, - Cfg::Not(ref child) => match **child { - Cfg::Cfg(..) => true, - _ => false, - }, + Cfg::Not(box Cfg::Cfg(..)) => true, + Cfg::Not(..) => false, } } fn should_use_with_in_description(&self) -> bool { - match *self { - Cfg::Cfg(name, _) if name == sym::target_feature => true, - _ => false, - } + matches!(self, Cfg::Cfg(sym::target_feature, _)) } /// Attempt to simplify this cfg by assuming that `assume` is already known to be true, will diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index be4c62d891..2588c00f2c 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -5,7 +5,7 @@ use std::iter::once; use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_hir::Mutability; use rustc_metadata::creader::LoadedMacro; @@ -17,7 +17,6 @@ use rustc_span::Span; use crate::clean::{self, Attributes, GetDefId, ToSource, TypeKind}; use crate::core::DocContext; -use crate::doctree; use super::Clean; @@ -57,7 +56,7 @@ crate fn try_inline( let kind = match res { Res::Def(DefKind::Trait, did) => { record_extern_fqn(cx, did, clean::TypeKind::Trait); - ret.extend(build_impls(cx, Some(parent_module), did, attrs)); + build_impls(cx, Some(parent_module), did, attrs, &mut ret); clean::TraitItem(build_external_trait(cx, did)) } Res::Def(DefKind::Fn, did) => { @@ -66,27 +65,27 @@ crate fn try_inline( } Res::Def(DefKind::Struct, did) => { record_extern_fqn(cx, did, clean::TypeKind::Struct); - ret.extend(build_impls(cx, Some(parent_module), did, attrs)); + build_impls(cx, Some(parent_module), did, attrs, &mut ret); clean::StructItem(build_struct(cx, did)) } Res::Def(DefKind::Union, did) => { record_extern_fqn(cx, did, clean::TypeKind::Union); - ret.extend(build_impls(cx, Some(parent_module), did, attrs)); + build_impls(cx, Some(parent_module), did, attrs, &mut ret); clean::UnionItem(build_union(cx, did)) } Res::Def(DefKind::TyAlias, did) => { record_extern_fqn(cx, did, clean::TypeKind::Typedef); - ret.extend(build_impls(cx, Some(parent_module), did, attrs)); + build_impls(cx, Some(parent_module), did, attrs, &mut ret); clean::TypedefItem(build_type_alias(cx, did), false) } Res::Def(DefKind::Enum, did) => { record_extern_fqn(cx, did, clean::TypeKind::Enum); - ret.extend(build_impls(cx, Some(parent_module), did, attrs)); + build_impls(cx, Some(parent_module), did, attrs, &mut ret); clean::EnumItem(build_enum(cx, did)) } Res::Def(DefKind::ForeignTy, did) => { record_extern_fqn(cx, did, clean::TypeKind::Foreign); - ret.extend(build_impls(cx, Some(parent_module), did, attrs)); + build_impls(cx, Some(parent_module), did, attrs, &mut ret); clean::ForeignTypeItem } // Never inline enum variants but leave them shown as re-exports. @@ -121,7 +120,7 @@ crate fn try_inline( }; let target_attrs = load_attrs(cx, did); - let attrs = merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone); + let attrs = box merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone); cx.renderinfo.borrow_mut().inlined.insert(did); let what_rustc_thinks = clean::Item::from_def_id_and_parts(did, Some(name), kind, cx); @@ -134,10 +133,7 @@ crate fn try_inline_glob( res: Res, visited: &mut FxHashSet, ) -> Option> { - if res == Res::Err { - return None; - } - let did = res.def_id(); + let did = res.opt_def_id()?; if did.is_local() { return None; } @@ -169,7 +165,17 @@ crate fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKin if !s.is_empty() { Some(s) } else { None } }); let fqn = if let clean::TypeKind::Macro = kind { - vec![crate_name, relative.last().expect("relative was empty")] + // Check to see if it is a macro 2.0 or built-in macro + if matches!( + cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())), + LoadedMacro::MacroDef(def, _) + if matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) + if !ast_def.macro_rules) + ) { + once(crate_name).chain(relative).collect() + } else { + vec![crate_name, relative.last().expect("relative was empty")] + } } else { once(crate_name).chain(relative).collect() }; @@ -236,11 +242,7 @@ fn build_struct(cx: &DocContext<'_>, did: DefId) -> clean::Struct { let variant = cx.tcx.adt_def(did).non_enum_variant(); clean::Struct { - struct_type: match variant.ctor_kind { - CtorKind::Fictive => doctree::Plain, - CtorKind::Fn => doctree::Tuple, - CtorKind::Const => doctree::Unit, - }, + struct_type: variant.ctor_kind, generics: (cx.tcx.generics_of(did), predicates).clean(cx), fields: variant.fields.clean(cx), fields_stripped: false, @@ -252,7 +254,6 @@ fn build_union(cx: &DocContext<'_>, did: DefId) -> clean::Union { let variant = cx.tcx.adt_def(did).non_enum_variant(); clean::Union { - struct_type: doctree::Plain, generics: (cx.tcx.generics_of(did), predicates).clean(cx), fields: variant.fields.clean(cx), fields_stripped: false, @@ -261,26 +262,12 @@ fn build_union(cx: &DocContext<'_>, did: DefId) -> clean::Union { fn build_type_alias(cx: &DocContext<'_>, did: DefId) -> clean::Typedef { let predicates = cx.tcx.explicit_predicates_of(did); + let type_ = cx.tcx.type_of(did).clean(cx); clean::Typedef { - type_: cx.tcx.type_of(did).clean(cx), + type_, generics: (cx.tcx.generics_of(did), predicates).clean(cx), - item_type: build_type_alias_type(cx, did), - } -} - -fn build_type_alias_type(cx: &DocContext<'_>, did: DefId) -> Option { - let type_ = cx.tcx.type_of(did).clean(cx); - type_.def_id().and_then(|did| build_ty(cx, did)) -} - -crate fn build_ty(cx: &DocContext<'_>, did: DefId) -> Option { - match cx.tcx.def_kind(did) { - DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::Const | DefKind::Static => { - Some(cx.tcx.type_of(did).clean(cx)) - } - DefKind::TyAlias => build_type_alias_type(cx, did), - _ => None, + item_type: None, } } @@ -290,16 +277,14 @@ crate fn build_impls( parent_module: Option, did: DefId, attrs: Option>, -) -> Vec { + ret: &mut Vec, +) { let tcx = cx.tcx; - let mut impls = Vec::new(); // for each implementation of an item represented by `did`, build the clean::Item for that impl for &did in tcx.inherent_impls(did).iter() { - build_impl(cx, parent_module, did, attrs, &mut impls); + build_impl(cx, parent_module, did, attrs, ret); } - - impls } /// `parent_module` refers to the parent of the re-export, not the original item @@ -362,18 +347,16 @@ crate fn build_impl( let impl_item = match did.as_local() { Some(did) => { let hir_id = tcx.hir().local_def_id_to_hir_id(did); - match tcx.hir().expect_item(hir_id).kind { - hir::ItemKind::Impl { self_ty, ref generics, ref items, .. } => { - Some((self_ty, generics, items)) - } + match &tcx.hir().expect_item(hir_id).kind { + hir::ItemKind::Impl(impl_) => Some(impl_), _ => panic!("`DefID` passed to `build_impl` is not an `impl"), } } None => None, }; - let for_ = match impl_item { - Some((self_ty, _, _)) => self_ty.clean(cx), + let for_ = match &impl_item { + Some(impl_) => impl_.self_ty.clean(cx), None => tcx.type_of(did).clean(cx), }; @@ -395,9 +378,13 @@ crate fn build_impl( let predicates = tcx.explicit_predicates_of(did); let (trait_items, generics) = match impl_item { - Some((_, generics, items)) => ( - items.iter().map(|item| tcx.hir().impl_item(item.id).clean(cx)).collect::>(), - generics.clean(cx), + Some(impl_) => ( + impl_ + .items + .iter() + .map(|item| tcx.hir().impl_item(item.id).clean(cx)) + .collect::>(), + impl_.generics.clean(cx), ), None => ( tcx.associated_items(did) @@ -442,73 +429,64 @@ crate fn build_impl( trait_, for_, items: trait_items, - polarity: Some(polarity.clean(cx)), + negative_polarity: polarity.clean(cx), synthetic: false, blanket_impl: None, }), cx, ); - item.attrs = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs); + item.attrs = box merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs); debug!("merged_attrs={:?}", item.attrs); ret.push(item); } fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) -> clean::Module { let mut items = Vec::new(); - fill_in(cx, did, &mut items, visited); - return clean::Module { items, is_crate: false }; - - fn fill_in( - cx: &DocContext<'_>, - did: DefId, - items: &mut Vec, - visited: &mut FxHashSet, - ) { - // If we're re-exporting a re-export it may actually re-export something in - // two namespaces, so the target may be listed twice. Make sure we only - // visit each node at most once. - for &item in cx.tcx.item_children(did).iter() { - if item.vis == ty::Visibility::Public { - if let Some(def_id) = item.res.mod_def_id() { - if did == def_id || !visited.insert(def_id) { - continue; - } + + // If we're re-exporting a re-export it may actually re-export something in + // two namespaces, so the target may be listed twice. Make sure we only + // visit each node at most once. + for &item in cx.tcx.item_children(did).iter() { + if item.vis == ty::Visibility::Public { + if let Some(def_id) = item.res.mod_def_id() { + if did == def_id || !visited.insert(def_id) { + continue; } - if let Res::PrimTy(p) = item.res { - // Primitive types can't be inlined so generate an import instead. - items.push(clean::Item { - name: None, - attrs: clean::Attributes::default(), - source: clean::Span::dummy(), - def_id: DefId::local(CRATE_DEF_INDEX), - visibility: clean::Public, - kind: clean::ImportItem(clean::Import::new_simple( - item.ident.name, - clean::ImportSource { - path: clean::Path { - global: false, - res: item.res, - segments: vec![clean::PathSegment { - name: clean::PrimitiveType::from(p).as_sym(), - args: clean::GenericArgs::AngleBracketed { - args: Vec::new(), - bindings: Vec::new(), - }, - }], - }, - did: None, + } + if let Res::PrimTy(p) = item.res { + // Primitive types can't be inlined so generate an import instead. + items.push(clean::Item { + name: None, + attrs: box clean::Attributes::default(), + source: clean::Span::dummy(), + def_id: DefId::local(CRATE_DEF_INDEX), + visibility: clean::Public, + kind: box clean::ImportItem(clean::Import::new_simple( + item.ident.name, + clean::ImportSource { + path: clean::Path { + global: false, + res: item.res, + segments: vec![clean::PathSegment { + name: clean::PrimitiveType::from(p).as_sym(), + args: clean::GenericArgs::AngleBracketed { + args: Vec::new(), + bindings: Vec::new(), + }, + }], }, - true, - )), - }); - } else if let Some(i) = - try_inline(cx, did, item.res, item.ident.name, None, visited) - { - items.extend(i) - } + did: None, + }, + true, + )), + }); + } else if let Some(i) = try_inline(cx, did, item.res, item.ident.name, None, visited) { + items.extend(i) } } } + + clean::Module { items, is_crate: false } } crate fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2ac8950bca..d14f150514 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -219,7 +219,6 @@ impl Clean for CrateNum { impl Clean for doctree::Module<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { let mut items: Vec = vec![]; - items.extend(self.imports.iter().flat_map(|x| x.clean(cx))); items.extend(self.foreigns.iter().map(|x| x.clean(cx))); items.extend(self.mods.iter().map(|x| x.clean(cx))); items.extend(self.items.iter().map(|x| x.clean(cx)).flatten()); @@ -466,20 +465,20 @@ impl Clean for hir::WherePredicate<'_> { impl<'a> Clean> for ty::Predicate<'a> { fn clean(&self, cx: &DocContext<'_>) -> Option { - let bound_predicate = self.bound_atom(); + let bound_predicate = self.kind(); match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(pred, _) => Some(bound_predicate.rebind(pred).clean(cx)), - ty::PredicateAtom::RegionOutlives(pred) => pred.clean(cx), - ty::PredicateAtom::TypeOutlives(pred) => pred.clean(cx), - ty::PredicateAtom::Projection(pred) => Some(pred.clean(cx)), - - ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => panic!("not user writable"), + ty::PredicateKind::Trait(pred, _) => Some(bound_predicate.rebind(pred).clean(cx)), + ty::PredicateKind::RegionOutlives(pred) => pred.clean(cx), + ty::PredicateKind::TypeOutlives(pred) => pred.clean(cx), + ty::PredicateKind::Projection(pred) => Some(pred.clean(cx)), + + ty::PredicateKind::Subtype(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => panic!("not user writable"), } } } @@ -607,11 +606,12 @@ impl Clean for hir::GenericParam<'_> { synthetic, }, ), - hir::GenericParamKind::Const { ref ty } => ( + hir::GenericParamKind::Const { ref ty, default: _ } => ( self.name.ident().name, GenericParamDefKind::Const { did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), ty: ty.clean(cx), + // FIXME(const_generics_defaults): add `default` field here for docs }, ), }; @@ -639,10 +639,10 @@ impl Clean for hir::Generics<'_> { /// /// [`lifetime_to_generic_param`]: rustc_ast_lowering::LoweringContext::lifetime_to_generic_param fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { - match param.kind { - hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided } => true, - _ => false, - } + matches!( + param.kind, + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided } + ) } let impl_trait_params = self @@ -743,19 +743,19 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx .flat_map(|(p, _)| { let mut projection = None; let param_idx = (|| { - let bound_p = p.bound_atom(); + let bound_p = p.kind(); match bound_p.skip_binder() { - ty::PredicateAtom::Trait(pred, _constness) => { + ty::PredicateKind::Trait(pred, _constness) => { if let ty::Param(param) = pred.self_ty().kind() { return Some(param.index); } } - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { if let ty::Param(param) = ty.kind() { return Some(param.index); } } - ty::PredicateAtom::Projection(p) => { + ty::PredicateKind::Projection(p) => { if let ty::Param(param) = p.projection_ty.self_ty().kind() { projection = Some(bound_p.rebind(p)); return Some(param.index); @@ -800,7 +800,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx for (param, mut bounds) in impl_trait { // Move trait bounds to the front. - bounds.sort_by_key(|b| if let GenericBound::TraitBound(..) = b { false } else { true }); + bounds.sort_by_key(|b| !matches!(b, GenericBound::TraitBound(..))); if let crate::core::ImplTraitParam::ParamIndex(idx) = param { if let Some(proj) = impl_trait_proj.remove(&idx) { @@ -831,7 +831,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx where_predicates.retain(|pred| match *pred { WP::BoundPredicate { ty: Generic(ref g), ref bounds } => { if bounds.iter().any(|b| b.is_sized_bound(cx)) { - sized_params.insert(g.clone()); + sized_params.insert(*g); false } else { true @@ -847,7 +847,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx && !sized_params.contains(&tp.name) { where_predicates.push(WP::BoundPredicate { - ty: Type::Generic(tp.name.clone()), + ty: Type::Generic(tp.name), bounds: vec![GenericBound::maybe_sized(cx)], }) } @@ -941,7 +941,7 @@ impl<'a> Clean for (&'a [hir::Ty<'a>], &'a [Ident]) { .iter() .enumerate() .map(|(i, ty)| { - let mut name = self.1.get(i).map(|ident| ident.name).unwrap_or(kw::Invalid); + let mut name = self.1.get(i).map_or(kw::Empty, |ident| ident.name); if name.is_empty() { name = kw::Underscore; } @@ -1000,7 +1000,7 @@ impl<'tcx> Clean for (DefId, ty::PolyFnSig<'tcx>) { .iter() .map(|t| Argument { type_: t.clean(cx), - name: names.next().map(|i| i.name).unwrap_or(kw::Invalid), + name: names.next().map_or(kw::Empty, |i| i.name), }) .collect(), }, @@ -1121,18 +1121,25 @@ impl Clean for hir::ImplItem<'_> { } MethodItem(m, Some(self.defaultness)) } - hir::ImplItemKind::TyAlias(ref ty) => { - let type_ = ty.clean(cx); - let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did)); - TypedefItem(Typedef { type_, generics: Generics::default(), item_type }, true) + hir::ImplItemKind::TyAlias(ref hir_ty) => { + let type_ = hir_ty.clean(cx); + let item_type = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx); + TypedefItem( + Typedef { + type_, + generics: Generics::default(), + item_type: Some(item_type), + }, + true, + ) } }; let what_rustc_thinks = Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx); let parent_item = cx.tcx.hir().expect_item(cx.tcx.hir().get_parent_item(self.hir_id)); - if let hir::ItemKind::Impl { of_trait, .. } = &parent_item.kind { - if of_trait.is_some() { + if let hir::ItemKind::Impl(impl_) = &parent_item.kind { + if impl_.of_trait.is_some() { // Trait impl items always inherit the impl's visibility -- // we don't want to show `pub`. Item { visibility: Inherited, ..what_rustc_thinks } @@ -1284,13 +1291,13 @@ impl Clean for ty::AssocItem { AssocTypeItem(bounds, ty.clean(cx)) } else { + // FIXME: when could this happen? ASsociated items in inherent impls? let type_ = cx.tcx.type_of(self.def_id).clean(cx); - let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did)); TypedefItem( Typedef { type_, generics: Generics { params: Vec::new(), where_predicates: Vec::new() }, - item_type, + item_type: None, }, true, ) @@ -1337,7 +1344,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &DocContext<'_>) -> Type { let mut ty_substs = FxHashMap::default(); let mut lt_substs = FxHashMap::default(); let mut ct_substs = FxHashMap::default(); - let generic_args = provided_params.generic_args(); + let generic_args = provided_params.args(); { let mut indices: GenericParamCount = Default::default(); for param in generics.params.iter() { @@ -1402,7 +1409,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &DocContext<'_>) -> Type { if let Some(ct) = const_ { ct_substs.insert(const_param_def_id.to_def_id(), ct.clean(cx)); } - // FIXME(const_generics:defaults) + // FIXME(const_generics_defaults) indices.consts += 1; } } @@ -1694,14 +1701,12 @@ impl<'tcx> Clean for Ty<'tcx> { let mut bounds = bounds .iter() .filter_map(|bound| { - // Note: The substs of opaque types can contain unbound variables, - // meaning that we have to use `ignore_quantifiers_with_unbound_vars` here. - let bound_predicate = bound.bound_atom_with_opt_escaping(cx.tcx); + let bound_predicate = bound.kind(); let trait_ref = match bound_predicate.skip_binder() { - ty::PredicateAtom::Trait(tr, _constness) => { + ty::PredicateKind::Trait(tr, _constness) => { bound_predicate.rebind(tr.trait_ref) } - ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { if let Some(r) = reg.clean(cx) { regions.push(GenericBound::Outlives(r)); } @@ -1720,8 +1725,8 @@ impl<'tcx> Clean for Ty<'tcx> { let bounds: Vec<_> = bounds .iter() .filter_map(|bound| { - if let ty::PredicateAtom::Projection(proj) = - bound.bound_atom_with_opt_escaping(cx.tcx).skip_binder() + if let ty::PredicateKind::Projection(proj) = + bound.kind().skip_binder() { if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() @@ -1839,35 +1844,22 @@ impl Clean for ty::Visibility { impl Clean for rustc_hir::VariantData<'_> { fn clean(&self, cx: &DocContext<'_>) -> VariantStruct { VariantStruct { - struct_type: doctree::struct_type_from_def(self), + struct_type: CtorKind::from_hir(self), fields: self.fields().iter().map(|x| x.clean(cx)).collect(), fields_stripped: false, } } } -impl Clean for doctree::Variant<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Item { - let what_rustc_thinks = Item::from_hir_id_and_parts( - self.id, - Some(self.name), - VariantItem(Variant { kind: self.def.clean(cx) }), - cx, - ); - // don't show `pub` for variants, which are always public - Item { visibility: Inherited, ..what_rustc_thinks } - } -} - impl Clean for ty::VariantDef { fn clean(&self, cx: &DocContext<'_>) -> Item { let kind = match self.ctor_kind { - CtorKind::Const => VariantKind::CLike, - CtorKind::Fn => VariantKind::Tuple( + CtorKind::Const => Variant::CLike, + CtorKind::Fn => Variant::Tuple( self.fields.iter().map(|f| cx.tcx.type_of(f.did).clean(cx)).collect(), ), - CtorKind::Fictive => VariantKind::Struct(VariantStruct { - struct_type: doctree::Plain, + CtorKind::Fictive => Variant::Struct(VariantStruct { + struct_type: CtorKind::Fictive, fields_stripped: false, fields: self .fields @@ -1883,25 +1875,21 @@ impl Clean for ty::VariantDef { .collect(), }), }; - let what_rustc_thinks = Item::from_def_id_and_parts( - self.def_id, - Some(self.ident.name), - VariantItem(Variant { kind }), - cx, - ); + let what_rustc_thinks = + Item::from_def_id_and_parts(self.def_id, Some(self.ident.name), VariantItem(kind), cx); // don't show `pub` for fields, which are always public Item { visibility: Inherited, ..what_rustc_thinks } } } -impl Clean for hir::VariantData<'_> { - fn clean(&self, cx: &DocContext<'_>) -> VariantKind { +impl Clean for hir::VariantData<'_> { + fn clean(&self, cx: &DocContext<'_>) -> Variant { match self { - hir::VariantData::Struct(..) => VariantKind::Struct(self.clean(cx)), + hir::VariantData::Struct(..) => Variant::Struct(self.clean(cx)), hir::VariantData::Tuple(..) => { - VariantKind::Tuple(self.fields().iter().map(|x| x.ty.clean(cx)).collect()) + Variant::Tuple(self.fields().iter().map(|x| x.ty.clean(cx)).collect()) } - hir::VariantData::Unit(..) => VariantKind::CLike, + hir::VariantData::Unit(..) => Variant::CLike, } } } @@ -1952,7 +1940,7 @@ impl Clean for hir::GenericArgs<'_> { impl Clean for hir::PathSegment<'_> { fn clean(&self, cx: &DocContext<'_>) -> PathSegment { - PathSegment { name: self.ident.name, args: self.generic_args().clean(cx) } + PathSegment { name: self.ident.name, args: self.args().clean(cx) } } } @@ -2003,11 +1991,15 @@ impl Clean> for (&hir::Item<'_>, Option) { bounds: ty.bounds.clean(cx), generics: ty.generics.clean(cx), }), - ItemKind::TyAlias(ty, ref generics) => { - let rustdoc_ty = ty.clean(cx); - let item_type = rustdoc_ty.def_id().and_then(|did| inline::build_ty(cx, did)); + ItemKind::TyAlias(hir_ty, ref generics) => { + let rustdoc_ty = hir_ty.clean(cx); + let ty = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx); TypedefItem( - Typedef { type_: rustdoc_ty, generics: generics.clean(cx), item_type }, + Typedef { + type_: rustdoc_ty, + generics: generics.clean(cx), + item_type: Some(ty), + }, false, ) } @@ -2021,23 +2013,22 @@ impl Clean> for (&hir::Item<'_>, Option) { bounds: bounds.clean(cx), }), ItemKind::Union(ref variant_data, ref generics) => UnionItem(Union { - struct_type: doctree::struct_type_from_def(&variant_data), generics: generics.clean(cx), fields: variant_data.fields().clean(cx), fields_stripped: false, }), ItemKind::Struct(ref variant_data, ref generics) => StructItem(Struct { - struct_type: doctree::struct_type_from_def(&variant_data), + struct_type: CtorKind::from_hir(variant_data), generics: generics.clean(cx), fields: variant_data.fields().clean(cx), fields_stripped: false, }), - ItemKind::Impl { .. } => return clean_impl(item, cx), + ItemKind::Impl(ref impl_) => return clean_impl(impl_, item.hir_id, cx), // proc macros can have a name set by attributes ItemKind::Fn(ref sig, ref generics, body_id) => { clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) } - hir::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref item_ids) => { + ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref item_ids) => { let items = item_ids .iter() .map(|ti| cx.tcx.hir().trait_item(ti.id).clean(cx)) @@ -2056,6 +2047,9 @@ impl Clean> for (&hir::Item<'_>, Option) { ItemKind::ExternCrate(orig_name) => { return clean_extern_crate(item, name, orig_name, cx); } + ItemKind::Use(path, kind) => { + return clean_use_statement(item, name, path, kind, cx); + } _ => unreachable!("not yet converted"), }; @@ -2066,7 +2060,7 @@ impl Clean> for (&hir::Item<'_>, Option) { impl Clean for hir::Variant<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { - let kind = VariantItem(Variant { kind: self.data.clean(cx) }); + let kind = VariantItem(self.data.clean(cx)); let what_rustc_thinks = Item::from_hir_id_and_parts(self.id, Some(self.ident.name), kind, cx); // don't show `pub` for variants, which are always public @@ -2074,28 +2068,24 @@ impl Clean for hir::Variant<'_> { } } -impl Clean for ty::ImplPolarity { - fn clean(&self, _: &DocContext<'_>) -> ImplPolarity { +impl Clean for ty::ImplPolarity { + /// Returns whether the impl has negative polarity. + fn clean(&self, _: &DocContext<'_>) -> bool { match self { &ty::ImplPolarity::Positive | // FIXME: do we want to do something else here? - &ty::ImplPolarity::Reservation => ImplPolarity::Positive, - &ty::ImplPolarity::Negative => ImplPolarity::Negative, + &ty::ImplPolarity::Reservation => false, + &ty::ImplPolarity::Negative => true, } } } -fn clean_impl(impl_: &hir::Item<'_>, cx: &DocContext<'_>) -> Vec { +fn clean_impl(impl_: &hir::Impl<'_>, hir_id: hir::HirId, cx: &DocContext<'_>) -> Vec { let mut ret = Vec::new(); - let (trait_, items, for_, unsafety, generics) = match &impl_.kind { - hir::ItemKind::Impl { of_trait, items, self_ty, unsafety, generics, .. } => { - (of_trait, items, self_ty, *unsafety, generics) - } - _ => unreachable!(), - }; - let trait_ = trait_.clean(cx); - let items = items.iter().map(|ii| cx.tcx.hir().impl_item(ii.id).clean(cx)).collect::>(); - let def_id = cx.tcx.hir().local_def_id(impl_.hir_id); + let trait_ = impl_.of_trait.clean(cx); + let items = + impl_.items.iter().map(|ii| cx.tcx.hir().impl_item(ii.id).clean(cx)).collect::>(); + let def_id = cx.tcx.hir().local_def_id(hir_id); // If this impl block is an implementation of the Deref trait, then we // need to try inlining the target's inherent impl blocks as well. @@ -2108,24 +2098,24 @@ fn clean_impl(impl_: &hir::Item<'_>, cx: &DocContext<'_>) -> Vec { .map(|did| cx.tcx.provided_trait_methods(did).map(|meth| meth.ident.name).collect()) .unwrap_or_default(); - let for_ = for_.clean(cx); + let for_ = impl_.self_ty.clean(cx); let type_alias = for_.def_id().and_then(|did| match cx.tcx.def_kind(did) { DefKind::TyAlias => Some(cx.tcx.type_of(did).clean(cx)), _ => None, }); let make_item = |trait_: Option, for_: Type, items: Vec| { let kind = ImplItem(Impl { - unsafety, - generics: generics.clean(cx), + unsafety: impl_.unsafety, + generics: impl_.generics.clean(cx), provided_trait_methods: provided.clone(), trait_, for_, items, - polarity: Some(cx.tcx.impl_polarity(def_id).clean(cx)), + negative_polarity: cx.tcx.impl_polarity(def_id).clean(cx), synthetic: false, blanket_impl: None, }); - Item::from_hir_id_and_parts(impl_.hir_id, None, kind, cx) + Item::from_hir_id_and_parts(hir_id, None, kind, cx) }; if let Some(type_alias) = type_alias { ret.push(make_item(trait_.clone(), type_alias, items.clone())); @@ -2173,99 +2163,105 @@ fn clean_extern_crate( // FIXME: using `from_def_id_and_kind` breaks `rustdoc/masked` for some reason vec![Item { name: None, - attrs: krate.attrs.clean(cx), + attrs: box krate.attrs.clean(cx), source: krate.span.clean(cx), def_id: crate_def_id, visibility: krate.vis.clean(cx), - kind: ExternCrateItem(name, orig_name), + kind: box ExternCrateItem(name, orig_name), }] } -impl Clean> for doctree::Import<'_> { - fn clean(&self, cx: &DocContext<'_>) -> Vec { - // We need this comparison because some imports (for std types for example) - // are "inserted" as well but directly by the compiler and they should not be - // taken into account. - if self.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) { - return Vec::new(); - } +fn clean_use_statement( + import: &hir::Item<'_>, + name: Symbol, + path: &hir::Path<'_>, + kind: hir::UseKind, + cx: &DocContext<'_>, +) -> Vec { + // We need this comparison because some imports (for std types for example) + // are "inserted" as well but directly by the compiler and they should not be + // taken into account. + if import.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) { + return Vec::new(); + } + + let (doc_meta_item, please_inline) = import.attrs.lists(sym::doc).get_word_attr(sym::inline); + let pub_underscore = import.vis.node.is_pub() && name == kw::Underscore; + + if pub_underscore && please_inline { + rustc_errors::struct_span_err!( + cx.tcx.sess, + doc_meta_item.unwrap().span(), + E0780, + "anonymous imports cannot be inlined" + ) + .span_label(import.span, "anonymous import") + .emit(); + } - // We consider inlining the documentation of `pub use` statements, but we - // forcefully don't inline if this is not public or if the - // #[doc(no_inline)] attribute is present. - // Don't inline doc(hidden) imports so they can be stripped at a later stage. - let mut denied = !self.vis.node.is_pub() - || self.attrs.iter().any(|a| { - a.has_name(sym::doc) - && match a.meta_item_list() { - Some(l) => { - attr::list_contains_name(&l, sym::no_inline) - || attr::list_contains_name(&l, sym::hidden) - } - None => false, + // We consider inlining the documentation of `pub use` statements, but we + // forcefully don't inline if this is not public or if the + // #[doc(no_inline)] attribute is present. + // Don't inline doc(hidden) imports so they can be stripped at a later stage. + let mut denied = !import.vis.node.is_pub() + || pub_underscore + || import.attrs.iter().any(|a| { + a.has_name(sym::doc) + && match a.meta_item_list() { + Some(l) => { + attr::list_contains_name(&l, sym::no_inline) + || attr::list_contains_name(&l, sym::hidden) } - }); - // Also check whether imports were asked to be inlined, in case we're trying to re-export a - // crate in Rust 2018+ - let please_inline = self.attrs.lists(sym::doc).has_word(sym::inline); - let path = self.path.clean(cx); - let inner = if self.glob { - if !denied { - let mut visited = FxHashSet::default(); - if let Some(items) = inline::try_inline_glob(cx, path.res, &mut visited) { - return items; + None => false, } + }); + + // Also check whether imports were asked to be inlined, in case we're trying to re-export a + // crate in Rust 2018+ + let def_id = cx.tcx.hir().local_def_id(import.hir_id).to_def_id(); + let path = path.clean(cx); + let inner = if kind == hir::UseKind::Glob { + if !denied { + let mut visited = FxHashSet::default(); + if let Some(items) = inline::try_inline_glob(cx, path.res, &mut visited) { + return items; } - Import::new_glob(resolve_use_source(cx, path), true) - } else { - let name = self.name; - if !please_inline { - if let Res::Def(DefKind::Mod, did) = path.res { - if !did.is_local() && did.index == CRATE_DEF_INDEX { - // if we're `pub use`ing an extern crate root, don't inline it unless we - // were specifically asked for it - denied = true; - } + } + Import::new_glob(resolve_use_source(cx, path), true) + } else { + if !please_inline { + if let Res::Def(DefKind::Mod, did) = path.res { + if !did.is_local() && did.index == CRATE_DEF_INDEX { + // if we're `pub use`ing an extern crate root, don't inline it unless we + // were specifically asked for it + denied = true; } } - if !denied { - let mut visited = FxHashSet::default(); + } + if !denied { + let mut visited = FxHashSet::default(); - if let Some(mut items) = inline::try_inline( + if let Some(mut items) = inline::try_inline( + cx, + cx.tcx.parent_module(import.hir_id).to_def_id(), + path.res, + name, + Some(import.attrs), + &mut visited, + ) { + items.push(Item::from_def_id_and_parts( + def_id, + None, + ImportItem(Import::new_simple(name, resolve_use_source(cx, path), false)), cx, - cx.tcx.parent_module(self.id).to_def_id(), - path.res, - name, - Some(self.attrs), - &mut visited, - ) { - items.push(Item { - name: None, - attrs: self.attrs.clean(cx), - source: self.span.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), - visibility: self.vis.clean(cx), - kind: ImportItem(Import::new_simple( - self.name, - resolve_use_source(cx, path), - false, - )), - }); - return items; - } + )); + return items; } - Import::new_simple(name, resolve_use_source(cx, path), true) - }; + } + Import::new_simple(name, resolve_use_source(cx, path), true) + }; - vec![Item { - name: None, - attrs: self.attrs.clean(cx), - source: self.span.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), - visibility: self.vis.clean(cx), - kind: ImportItem(inner), - }] - } + vec![Item::from_def_id_and_parts(def_id, None, ImportItem(inner), cx)] } impl Clean for (&hir::ForeignItem<'_>, Option) { @@ -2333,14 +2329,14 @@ impl Clean for (&hir::MacroDef<'_>, Option) { if matchers.len() <= 1 { format!( "{}macro {}{} {{\n ...\n}}", - vis.print_with_space(cx.tcx, def_id), + vis.print_with_space(cx.tcx, def_id, &cx.cache), name, matchers.iter().map(|span| span.to_src(cx)).collect::(), ) } else { format!( "{}macro {} {{\n{}}}", - vis.print_with_space(cx.tcx, def_id), + vis.print_with_space(cx.tcx, def_id, &cx.cache), name, matchers .iter() diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 16aaa9cfd2..d4d0a8ce24 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -129,7 +129,7 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) .predicates .iter() .filter_map(|(pred, _)| { - if let ty::PredicateAtom::Trait(pred, _) = pred.skip_binders() { + if let ty::PredicateKind::Trait(pred, _) = pred.kind().skip_binder() { if pred.trait_ref.self_ty() == self_ty { Some(pred.def_id()) } else { None } } else { None diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index dd818a643e..e509ec3f02 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -8,20 +8,20 @@ use std::rc::Rc; use std::sync::Arc; use std::{slice, vec}; +use arrayvec::ArrayVec; use rustc_ast::attr; use rustc_ast::util::comments::beautify_doc_string; use rustc_ast::{self as ast, AttrStyle}; -use rustc_ast::{FloatTy, IntTy, UintTy}; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_feature::UnstableFeatures; use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::def::{CtorKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, DefIndex}; use rustc_hir::lang_items::LangItem; use rustc_hir::Mutability; use rustc_index::vec::IndexVec; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::DUMMY_SP; @@ -29,7 +29,6 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol, SymbolStr}; use rustc_span::{self, FileName, Loc}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; -use smallvec::{smallvec, SmallVec}; use crate::clean::cfg::Cfg; use crate::clean::external_path; @@ -37,8 +36,7 @@ use crate::clean::inline; use crate::clean::types::Type::{QPath, ResolvedPath}; use crate::clean::Clean; use crate::core::DocContext; -use crate::doctree; -use crate::formats::cache::cache; +use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::render::cache::ExternalLocation; @@ -47,7 +45,7 @@ use self::ItemKind::*; use self::SelfTy::*; use self::Type::*; -thread_local!(crate static MAX_DEF_ID: RefCell> = Default::default()); +thread_local!(crate static MAX_DEF_IDX: RefCell> = Default::default()); #[derive(Clone, Debug)] crate struct Crate { @@ -82,12 +80,16 @@ crate struct Item { crate source: Span, /// Not everything has a name. E.g., impls crate name: Option, - crate attrs: Attributes, + crate attrs: Box, crate visibility: Visibility, - crate kind: ItemKind, + crate kind: Box, crate def_id: DefId, } +// `Item` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +rustc_data_structures::static_assert_size!(Item, 48); + impl fmt::Debug for Item { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let def_id: &dyn fmt::Debug = if self.is_fake() { &"**FAKE**" } else { &self.def_id }; @@ -118,7 +120,7 @@ impl Item { /// Finds the `doc` attribute as a NameValue and returns the corresponding /// value found. - crate fn doc_value(&self) -> Option<&str> { + crate fn doc_value(&self) -> Option { self.attrs.doc_value() } @@ -152,10 +154,10 @@ impl Item { Item { def_id, - kind, + kind: box kind, name, source: source.clean(cx), - attrs: cx.tcx.get_attrs(def_id).clean(cx), + attrs: box cx.tcx.get_attrs(def_id).clean(cx), visibility: cx.tcx.visibility(def_id).clean(cx), } } @@ -166,16 +168,16 @@ impl Item { self.attrs.collapsed_doc_value() } - crate fn links(&self) -> Vec { - self.attrs.links(&self.def_id.krate) + crate fn links(&self, cache: &Cache) -> Vec { + self.attrs.links(&self.def_id.krate, cache) } crate fn is_crate(&self) -> bool { - match self.kind { + matches!( + *self.kind, StrippedItem(box ModuleItem(Module { is_crate: true, .. })) - | ModuleItem(Module { is_crate: true, .. }) => true, - _ => false, - } + | ModuleItem(Module { is_crate: true, .. }) + ) } crate fn is_mod(&self) -> bool { self.type_() == ItemType::Module @@ -223,19 +225,17 @@ impl Item { self.type_() == ItemType::Keyword } crate fn is_stripped(&self) -> bool { - match self.kind { + match *self.kind { StrippedItem(..) => true, ImportItem(ref i) => !i.should_be_displayed, _ => false, } } crate fn has_stripped_fields(&self) -> Option { - match self.kind { + match *self.kind { StructItem(ref _struct) => Some(_struct.fields_stripped), UnionItem(ref union) => Some(union.fields_stripped), - VariantItem(Variant { kind: VariantKind::Struct(ref vstruct) }) => { - Some(vstruct.fields_stripped) - } + VariantItem(Variant::Struct(ref vstruct)) => Some(vstruct.fields_stripped), _ => None, } } @@ -281,7 +281,7 @@ impl Item { } crate fn is_default(&self) -> bool { - match self.kind { + match *self.kind { ItemKind::MethodItem(_, Some(defaultness)) => { defaultness.has_value() && !defaultness.is_final() } @@ -289,10 +289,12 @@ impl Item { } } - /// See comments on next_def_id + /// See the documentation for [`next_def_id()`]. + /// + /// [`next_def_id()`]: DocContext::next_def_id() crate fn is_fake(&self) -> bool { - MAX_DEF_ID.with(|m| { - m.borrow().get(&self.def_id.krate).map(|id| self.def_id >= *id).unwrap_or(false) + MAX_DEF_IDX.with(|m| { + m.borrow().get(&self.def_id.krate).map(|&idx| idx <= self.def_id.index).unwrap_or(false) }) } } @@ -330,6 +332,10 @@ crate enum ItemKind { ProcMacroItem(ProcMacro), PrimitiveItem(PrimitiveType), AssocConstItem(Type, Option), + /// An associated item in a trait or trait impl. + /// + /// The bounds may be non-empty if there is a `where` clause. + /// The `Option` is the default concrete type (e.g. `trait Trait { type Target = usize; }`) AssocTypeItem(Vec, Option), /// An item that has been stripped by a rustdoc pass StrippedItem(Box), @@ -343,7 +349,7 @@ impl ItemKind { match self { StructItem(s) => s.fields.iter(), UnionItem(u) => u.fields.iter(), - VariantItem(Variant { kind: VariantKind::Struct(v) }) => v.fields.iter(), + VariantItem(Variant::Struct(v)) => v.fields.iter(), EnumItem(e) => e.variants.iter(), TraitItem(t) => t.items.iter(), ImplItem(i) => i.items.iter(), @@ -374,10 +380,7 @@ impl ItemKind { } crate fn is_type_alias(&self) -> bool { - match *self { - ItemKind::TypedefItem(_, _) | ItemKind::AssocTypeItem(_, _) => true, - _ => false, - } + matches!(self, ItemKind::TypedefItem(..) | ItemKind::AssocTypeItem(..)) } } @@ -435,12 +438,22 @@ impl AttributesExt for [ast::Attribute] { crate trait NestedAttributesExt { /// Returns `true` if the attribute list contains a specific `Word` fn has_word(self, word: Symbol) -> bool; + fn get_word_attr(self, word: Symbol) -> (Option, bool); } -impl> NestedAttributesExt for I { +impl + IntoIterator> + NestedAttributesExt for I +{ fn has_word(self, word: Symbol) -> bool { self.into_iter().any(|attr| attr.is_word() && attr.has_name(word)) } + + fn get_word_attr(mut self, word: Symbol) -> (Option, bool) { + match self.find(|attr| attr.is_word() && attr.has_name(word)) { + Some(a) => (Some(a), true), + None => (None, false), + } + } } /// A portion of documentation, extracted from a `#[doc]` attribute. @@ -460,11 +473,13 @@ crate struct DocFragment { /// This allows distinguishing between the original documentation and a pub re-export. /// If it is `None`, the item was not re-exported. crate parent_module: Option, - crate doc: String, + crate doc: Symbol, crate kind: DocFragmentKind, + crate need_backline: bool, + crate indent: usize, } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] crate enum DocFragmentKind { /// A doc fragment created from a `///` or `//!` doc comment. SugaredDoc, @@ -472,7 +487,33 @@ crate enum DocFragmentKind { RawDoc, /// A doc fragment created from a `#[doc(include="filename")]` attribute. Contains both the /// given filename and the file contents. - Include { filename: String }, + Include { filename: Symbol }, +} + +// The goal of this function is to apply the `DocFragment` transformations that are required when +// transforming into the final markdown. So the transformations in here are: +// +// * Applying the computed indent to each lines in each doc fragment (a `DocFragment` can contain +// multiple lines in case of `#[doc = ""]`). +// * Adding backlines between `DocFragment`s and adding an extra one if required (stored in the +// `need_backline` field). +fn add_doc_fragment(out: &mut String, frag: &DocFragment) { + let s = frag.doc.as_str(); + let mut iter = s.lines().peekable(); + while let Some(line) = iter.next() { + if line.chars().any(|c| !c.is_whitespace()) { + assert!(line.len() >= frag.indent); + out.push_str(&line[frag.indent..]); + } else { + out.push_str(line); + } + if iter.peek().is_some() { + out.push('\n'); + } + } + if frag.need_backline { + out.push('\n'); + } } impl<'a> FromIterator<&'a DocFragment> for String { @@ -480,11 +521,18 @@ impl<'a> FromIterator<&'a DocFragment> for String { where T: IntoIterator, { + let mut prev_kind: Option = None; iter.into_iter().fold(String::new(), |mut acc, frag| { - if !acc.is_empty() { + if !acc.is_empty() + && prev_kind + .take() + .map(|p| matches!(p, DocFragmentKind::Include { .. }) && p != frag.kind) + .unwrap_or(false) + { acc.push('\n'); } - acc.push_str(&frag.doc); + add_doc_fragment(&mut acc, &frag); + prev_kind = Some(frag.kind); acc }) } @@ -556,7 +604,7 @@ impl Attributes { /// Reads a `MetaItem` from within an attribute, looks for whether it is a /// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from /// its expansion. - crate fn extract_include(mi: &ast::MetaItem) -> Option<(String, String)> { + crate fn extract_include(mi: &ast::MetaItem) -> Option<(Symbol, Symbol)> { mi.meta_item_list().and_then(|list| { for meta in list { if meta.has_name(sym::include) { @@ -564,17 +612,17 @@ impl Attributes { // `#[doc(include(file="filename", contents="file contents")]` so we need to // look for that instead return meta.meta_item_list().and_then(|list| { - let mut filename: Option = None; - let mut contents: Option = None; + let mut filename: Option = None; + let mut contents: Option = None; for it in list { if it.has_name(sym::file) { if let Some(name) = it.value_str() { - filename = Some(name.to_string()); + filename = Some(name); } } else if it.has_name(sym::contents) { if let Some(docs) = it.value_str() { - contents = Some(docs.to_string()); + contents = Some(docs); } } } @@ -613,15 +661,30 @@ impl Attributes { attrs: &[ast::Attribute], additional_attrs: Option<(&[ast::Attribute], DefId)>, ) -> Attributes { - let mut doc_strings = vec![]; + let mut doc_strings: Vec = vec![]; let mut sp = None; let mut cfg = Cfg::True; let mut doc_line = 0; + fn update_need_backline(doc_strings: &mut Vec, frag: &DocFragment) { + if let Some(prev) = doc_strings.last_mut() { + if matches!(prev.kind, DocFragmentKind::Include { .. }) + || prev.kind != frag.kind + || prev.parent_module != frag.parent_module + { + // add a newline for extra padding between segments + prev.need_backline = prev.kind == DocFragmentKind::SugaredDoc + || prev.kind == DocFragmentKind::RawDoc + } else { + prev.need_backline = true; + } + } + } + let clean_attr = |(attr, parent_module): (&ast::Attribute, _)| { if let Some(value) = attr.doc_str() { trace!("got doc_str={:?}", value); - let value = beautify_doc_string(value).to_string(); + let value = beautify_doc_string(value); let kind = if attr.is_doc_comment() { DocFragmentKind::SugaredDoc } else { @@ -629,14 +692,20 @@ impl Attributes { }; let line = doc_line; - doc_line += value.lines().count(); - doc_strings.push(DocFragment { + doc_line += value.as_str().lines().count(); + let frag = DocFragment { line, span: attr.span, doc: value, kind, parent_module, - }); + need_backline: false, + indent: 0, + }; + + update_need_backline(&mut doc_strings, &frag); + + doc_strings.push(frag); if sp.is_none() { sp = Some(attr.span); @@ -654,14 +723,18 @@ impl Attributes { } else if let Some((filename, contents)) = Attributes::extract_include(&mi) { let line = doc_line; - doc_line += contents.lines().count(); - doc_strings.push(DocFragment { + doc_line += contents.as_str().lines().count(); + let frag = DocFragment { line, span: attr.span, doc: contents, kind: DocFragmentKind::Include { filename }, - parent_module: parent_module, - }); + parent_module, + need_backline: false, + indent: 0, + }; + update_need_backline(&mut doc_strings, &frag); + doc_strings.push(frag); } } } @@ -712,20 +785,47 @@ impl Attributes { /// Finds the `doc` attribute as a NameValue and returns the corresponding /// value found. - crate fn doc_value(&self) -> Option<&str> { - self.doc_strings.first().map(|s| s.doc.as_str()) + crate fn doc_value(&self) -> Option { + let mut iter = self.doc_strings.iter(); + + let ori = iter.next()?; + let mut out = String::new(); + add_doc_fragment(&mut out, &ori); + while let Some(new_frag) = iter.next() { + if matches!(ori.kind, DocFragmentKind::Include { .. }) + || new_frag.kind != ori.kind + || new_frag.parent_module != ori.parent_module + { + break; + } + add_doc_fragment(&mut out, &new_frag); + } + if out.is_empty() { None } else { Some(out) } + } + + /// Return the doc-comments on this item, grouped by the module they came from. + /// + /// The module can be different if this is a re-export with added documentation. + crate fn collapsed_doc_value_by_module_level(&self) -> FxHashMap, String> { + let mut ret = FxHashMap::default(); + + for new_frag in self.doc_strings.iter() { + let out = ret.entry(new_frag.parent_module).or_default(); + add_doc_fragment(out, &new_frag); + } + ret } /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined /// with newlines. crate fn collapsed_doc_value(&self) -> Option { - if !self.doc_strings.is_empty() { Some(self.doc_strings.iter().collect()) } else { None } + if self.doc_strings.is_empty() { None } else { Some(self.doc_strings.iter().collect()) } } /// Gets links as a vector /// /// Cache must be populated before call - crate fn links(&self, krate: &CrateNum) -> Vec { + crate fn links(&self, krate: &CrateNum, cache: &Cache) -> Vec { use crate::html::format::href; use crate::html::render::CURRENT_DEPTH; @@ -734,9 +834,9 @@ impl Attributes { .filter_map(|ItemLink { link: s, link_text, did, fragment }| { match *did { Some(did) => { - if let Some((mut href, ..)) = href(did) { + if let Some((mut href, ..)) = href(did, cache) { if let Some(ref fragment) = *fragment { - href.push_str("#"); + href.push('#'); href.push_str(fragment); } Some(RenderedLink { @@ -750,7 +850,6 @@ impl Attributes { } None => { if let Some(ref fragment) = *fragment { - let cache = cache(); let url = match cache.extern_locations.get(krate) { Some(&(_, _, ExternalLocation::Local)) => { let depth = CURRENT_DEPTH.with(|l| l.get()); @@ -931,10 +1030,7 @@ crate enum GenericParamDefKind { impl GenericParamDefKind { crate fn is_type(&self) -> bool { - match *self { - GenericParamDefKind::Type { .. } => true, - _ => false, - } + matches!(self, GenericParamDefKind::Type { .. }) } // FIXME(eddyb) this either returns the default of a type parameter, or the @@ -1079,6 +1175,13 @@ impl GetDefId for FnRetTy { DefaultReturn => None, } } + + fn def_id_full(&self, cache: &Cache) -> Option { + match *self { + Return(ref ty) => ty.def_id_full(cache), + DefaultReturn => None, + } + } } #[derive(Clone, Debug)] @@ -1127,6 +1230,7 @@ crate enum Type { BareFunction(Box), Tuple(Vec), Slice(Box), + /// The `String` field is about the size or the constant representing the array's length. Array(Box, String), Never, RawPointer(Mutability, Box), @@ -1197,16 +1301,35 @@ crate enum TypeKind { Attr, Derive, TraitAlias, + Primitive, } crate trait GetDefId { + /// Use this method to get the [`DefId`] of a [`clean`] AST node. + /// This will return [`None`] when called on a primitive [`clean::Type`]. + /// Use [`Self::def_id_full`] if you want to include primitives. + /// + /// [`clean`]: crate::clean + /// [`clean::Type`]: crate::clean::Type + // FIXME: get rid of this function and always use `def_id_full` fn def_id(&self) -> Option; + + /// Use this method to get the [DefId] of a [clean] AST node, including [PrimitiveType]s. + /// + /// See [`Self::def_id`] for more. + /// + /// [clean]: crate::clean + fn def_id_full(&self, cache: &Cache) -> Option; } impl GetDefId for Option { fn def_id(&self) -> Option { self.as_ref().and_then(|d| d.def_id()) } + + fn def_id_full(&self, cache: &Cache) -> Option { + self.as_ref().and_then(|d| d.def_id_full(cache)) + } } impl Type { @@ -1278,15 +1401,22 @@ impl Type { } crate fn is_full_generic(&self) -> bool { - match *self { - Type::Generic(_) => true, + matches!(self, Type::Generic(_)) + } + + crate fn is_primitive(&self) -> bool { + match self { + Self::Primitive(_) => true, + Self::BorrowedRef { ref type_, .. } | Self::RawPointer(_, ref type_) => { + type_.is_primitive() + } _ => false, } } crate fn projection(&self) -> Option<(&Type, DefId, Symbol)> { let (self_, trait_, name) = match self { - QPath { ref self_type, ref trait_, name } => (self_type, trait_, name), + QPath { self_type, trait_, name } => (self_type, trait_, name), _ => return None, }; let trait_did = match **trait_ { @@ -1297,35 +1427,45 @@ impl Type { } } -impl GetDefId for Type { - fn def_id(&self) -> Option { - match *self { - ResolvedPath { did, .. } => Some(did), - Primitive(p) => cache().primitive_locations.get(&p).cloned(), - BorrowedRef { type_: box Generic(..), .. } => { - Primitive(PrimitiveType::Reference).def_id() - } - BorrowedRef { ref type_, .. } => type_.def_id(), +impl Type { + fn inner_def_id(&self, cache: Option<&Cache>) -> Option { + let t: PrimitiveType = match *self { + ResolvedPath { did, .. } => return Some(did), + Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()), + BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, + BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache), Tuple(ref tys) => { if tys.is_empty() { - Primitive(PrimitiveType::Unit).def_id() + PrimitiveType::Unit } else { - Primitive(PrimitiveType::Tuple).def_id() + PrimitiveType::Tuple } } - BareFunction(..) => Primitive(PrimitiveType::Fn).def_id(), - Never => Primitive(PrimitiveType::Never).def_id(), - Slice(..) => Primitive(PrimitiveType::Slice).def_id(), - Array(..) => Primitive(PrimitiveType::Array).def_id(), - RawPointer(..) => Primitive(PrimitiveType::RawPointer).def_id(), - QPath { ref self_type, .. } => self_type.def_id(), - _ => None, - } + BareFunction(..) => PrimitiveType::Fn, + Never => PrimitiveType::Never, + Slice(..) => PrimitiveType::Slice, + Array(..) => PrimitiveType::Array, + RawPointer(..) => PrimitiveType::RawPointer, + QPath { ref self_type, .. } => return self_type.inner_def_id(cache), + Generic(_) | Infer | ImplTrait(_) => return None, + }; + cache.and_then(|c| Primitive(t).def_id_full(c)) + } +} + +impl GetDefId for Type { + fn def_id(&self) -> Option { + self.inner_def_id(None) + } + + fn def_id_full(&self, cache: &Cache) -> Option { + self.inner_def_id(Some(cache)) } } impl PrimitiveType { crate fn from_hir(prim: hir::PrimTy) -> PrimitiveType { + use ast::{FloatTy, IntTy, UintTy}; match prim { hir::PrimTy::Int(IntTy::Isize) => PrimitiveType::Isize, hir::PrimTy::Int(IntTy::I8) => PrimitiveType::I8, @@ -1409,12 +1549,12 @@ impl PrimitiveType { } } - crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static SmallVec<[DefId; 4]> { + crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static ArrayVec<[DefId; 4]> { Self::all_impls(tcx).get(self).expect("missing impl for primitive type") } - crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap> { - static CELL: OnceCell>> = OnceCell::new(); + crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap> { + static CELL: OnceCell>> = OnceCell::new(); CELL.get_or_init(move || { use self::PrimitiveType::*; @@ -1438,7 +1578,7 @@ impl PrimitiveType { } let single = |a: Option| a.into_iter().collect(); - let both = |a: Option, b: Option| -> SmallVec<_> { + let both = |a: Option, b: Option| -> ArrayVec<_> { a.into_iter().chain(b).collect() }; @@ -1471,8 +1611,8 @@ impl PrimitiveType { .collect() }, Array => single(lang_items.array_impl()), - Tuple => smallvec![], - Unit => smallvec![], + Tuple => ArrayVec::new(), + Unit => ArrayVec::new(), RawPointer => { lang_items .const_ptr_impl() @@ -1482,9 +1622,9 @@ impl PrimitiveType { .chain(lang_items.mut_slice_ptr_impl()) .collect() }, - Reference => smallvec![], - Fn => smallvec![], - Never => smallvec![], + Reference => ArrayVec::new(), + Fn => ArrayVec::new(), + Never => ArrayVec::new(), } }) } @@ -1560,6 +1700,41 @@ impl From for PrimitiveType { } } +impl From for PrimitiveType { + fn from(int_ty: ty::IntTy) -> PrimitiveType { + match int_ty { + ty::IntTy::Isize => PrimitiveType::Isize, + ty::IntTy::I8 => PrimitiveType::I8, + ty::IntTy::I16 => PrimitiveType::I16, + ty::IntTy::I32 => PrimitiveType::I32, + ty::IntTy::I64 => PrimitiveType::I64, + ty::IntTy::I128 => PrimitiveType::I128, + } + } +} + +impl From for PrimitiveType { + fn from(uint_ty: ty::UintTy) -> PrimitiveType { + match uint_ty { + ty::UintTy::Usize => PrimitiveType::Usize, + ty::UintTy::U8 => PrimitiveType::U8, + ty::UintTy::U16 => PrimitiveType::U16, + ty::UintTy::U32 => PrimitiveType::U32, + ty::UintTy::U64 => PrimitiveType::U64, + ty::UintTy::U128 => PrimitiveType::U128, + } + } +} + +impl From for PrimitiveType { + fn from(float_ty: ty::FloatTy) -> PrimitiveType { + match float_ty { + ty::FloatTy::F32 => PrimitiveType::F32, + ty::FloatTy::F64 => PrimitiveType::F64, + } + } +} + impl From for PrimitiveType { fn from(prim_ty: hir::PrimTy) -> PrimitiveType { match prim_ty { @@ -1588,7 +1763,7 @@ impl Visibility { #[derive(Clone, Debug)] crate struct Struct { - crate struct_type: doctree::StructType, + crate struct_type: CtorKind, crate generics: Generics, crate fields: Vec, crate fields_stripped: bool, @@ -1596,7 +1771,6 @@ crate struct Struct { #[derive(Clone, Debug)] crate struct Union { - crate struct_type: doctree::StructType, crate generics: Generics, crate fields: Vec, crate fields_stripped: bool, @@ -1607,7 +1781,7 @@ crate struct Union { /// only as a variant in an enum. #[derive(Clone, Debug)] crate struct VariantStruct { - crate struct_type: doctree::StructType, + crate struct_type: CtorKind, crate fields: Vec, crate fields_stripped: bool, } @@ -1620,12 +1794,7 @@ crate struct Enum { } #[derive(Clone, Debug)] -crate struct Variant { - crate kind: VariantKind, -} - -#[derive(Clone, Debug)] -crate enum VariantKind { +crate enum Variant { CLike, Tuple(Vec), Struct(VariantStruct), @@ -1714,7 +1883,12 @@ crate struct PathSegment { crate struct Typedef { crate type_: Type, crate generics: Generics, - // Type of target item. + /// `type_` can come from either the HIR or from metadata. If it comes from HIR, it may be a type + /// alias instead of the final type. This will always have the final type, regardless of whether + /// `type_` came from HIR or from metadata. + /// + /// If `item_type.is_none()`, `type_` is guarenteed to come from metadata (and therefore hold the + /// final type). crate item_type: Option, } @@ -1722,6 +1896,10 @@ impl GetDefId for Typedef { fn def_id(&self) -> Option { self.type_.def_id() } + + fn def_id_full(&self, cache: &Cache) -> Option { + self.type_.def_id_full(cache) + } } #[derive(Clone, Debug)] @@ -1756,12 +1934,6 @@ crate struct Constant { crate is_literal: bool, } -#[derive(Clone, PartialEq, Debug)] -crate enum ImplPolarity { - Positive, - Negative, -} - #[derive(Clone, Debug)] crate struct Impl { crate unsafety: hir::Unsafety, @@ -1770,7 +1942,7 @@ crate struct Impl { crate trait_: Option, crate for_: Type, crate items: Vec, - crate polarity: Option, + crate negative_polarity: bool, crate synthetic: bool, crate blanket_impl: Option, } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 17f12d0a82..7784a5d234 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -8,7 +8,6 @@ use crate::clean::{ }; use crate::core::DocContext; -use itertools::Itertools; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -43,7 +42,7 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate { let mut module = module.clean(cx); let mut masked_crates = FxHashSet::default(); - match module.kind { + match *module.kind { ItemKind::ModuleItem(ref module) => { for it in &module.items { // `compiler_builtins` should be masked too, but we can't apply @@ -61,7 +60,7 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate { let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx); { - let m = match module.kind { + let m = match *module.kind { ItemKind::ModuleItem(ref mut m) => m, _ => unreachable!(), }; @@ -74,7 +73,7 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate { ) })); m.items.extend(keywords.into_iter().map(|(def_id, kw)| { - Item::from_def_id_and_parts(def_id, Some(kw.clone()), ItemKind::KeywordItem(kw), cx) + Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::KeywordItem(kw), cx) })); } @@ -172,20 +171,29 @@ crate fn get_real_types( cx: &DocContext<'_>, recurse: i32, ) -> FxHashSet<(Type, TypeKind)> { + fn insert(res: &mut FxHashSet<(Type, TypeKind)>, cx: &DocContext<'_>, ty: Type) { + if let Some(kind) = ty.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { + res.insert((ty, kind)); + } else if ty.is_primitive() { + // This is a primitive, let's store it as such. + res.insert((ty, TypeKind::Primitive)); + } + } let mut res = FxHashSet::default(); if recurse >= 10 { // FIXME: remove this whole recurse thing when the recursion bug is fixed return res; } + if arg.is_full_generic() { - let arg_s = Symbol::intern(&arg.print().to_string()); + let arg_s = Symbol::intern(&arg.print(&cx.cache).to_string()); if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g { - &WherePredicate::BoundPredicate { ref ty, .. } => ty.def_id() == arg.def_id(), + WherePredicate::BoundPredicate { ty, .. } => ty.def_id() == arg.def_id(), _ => false, }) { let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]); for bound in bounds.iter() { - if let GenericBound::TraitBound(ref poly_trait, _) = *bound { + if let GenericBound::TraitBound(poly_trait, _) = bound { for x in poly_trait.generic_params.iter() { if !x.is_type() { continue; @@ -195,11 +203,7 @@ crate fn get_real_types( if !adds.is_empty() { res.extend(adds); } else if !ty.is_full_generic() { - if let Some(kind) = - ty.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) - { - res.insert((ty, kind)); - } + insert(&mut res, cx, ty); } } } @@ -213,17 +217,13 @@ crate fn get_real_types( if !adds.is_empty() { res.extend(adds); } else if !ty.is_full_generic() { - if let Some(kind) = ty.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { - res.insert((ty.clone(), kind)); - } + insert(&mut res, cx, ty); } } } } } else { - if let Some(kind) = arg.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { - res.insert((arg.clone(), kind)); - } + insert(&mut res, cx, arg.clone()); if let Some(gens) = arg.generics() { for gen in gens.iter() { if gen.is_full_generic() { @@ -231,8 +231,8 @@ crate fn get_real_types( if !adds.is_empty() { res.extend(adds); } - } else if let Some(kind) = gen.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { - res.insert((gen.clone(), kind)); + } else { + insert(&mut res, cx, gen.clone()); } } } @@ -307,7 +307,7 @@ crate fn strip_path(path: &Path) -> Path { .segments .iter() .map(|s| PathSegment { - name: s.name.clone(), + name: s.name, args: GenericArgs::AngleBracketed { args: vec![], bindings: vec![] }, }) .collect(); @@ -338,24 +338,18 @@ crate fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut let tcx = cx.tcx; for item in items { - let target = match item.kind { + let target = match *item.kind { ItemKind::TypedefItem(ref t, true) => &t.type_, _ => continue, }; - let primitive = match *target { - ResolvedPath { did, .. } if did.is_local() => continue, - ResolvedPath { did, .. } => { - ret.extend(inline::build_impls(cx, None, did, None)); - continue; + + if let Some(prim) = target.primitive_type() { + for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) { + inline::build_impl(cx, None, did, None, ret); } - _ => match target.primitive_type() { - Some(prim) => prim, - None => continue, - }, - }; - for &did in primitive.impls(tcx) { + } else if let ResolvedPath { did, .. } = *target { if !did.is_local() { - inline::build_impl(cx, None, did, None, ret); + inline::build_impls(cx, None, did, None, ret); } } } @@ -415,10 +409,7 @@ crate fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { ); return Symbol::intern("()"); } - PatKind::Range(..) => panic!( - "tried to get argument name from PatKind::Range, \ - which is not allowed in function arguments" - ), + PatKind::Range(..) => return kw::Underscore, PatKind::Slice(ref begin, ref mid, ref end) => { let begin = begin.iter().map(|p| name_from_pat(&**p).to_string()); let mid = mid.as_ref().map(|p| format!("..{}", name_from_pat(&**p))).into_iter(); @@ -544,7 +535,7 @@ crate fn resolve_type(cx: &DocContext<'_>, path: Path, id: hir::HirId) -> Type { return Generic(kw::SelfUpper); } Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => { - return Generic(Symbol::intern(&format!("{:#}", path.print()))); + return Generic(Symbol::intern(&format!("{:#}", path.print(&cx.cache)))); } Res::SelfTy(..) | Res::Def(DefKind::TyParam | DefKind::AssocTy, _) => true, _ => false, @@ -558,12 +549,16 @@ crate fn get_auto_trait_and_blanket_impls( ty: Ty<'tcx>, param_env_def_id: DefId, ) -> impl Iterator { - let auto_impls = cx.sess().time("get_auto_trait_impls", || { - AutoTraitFinder::new(cx).get_auto_trait_impls(ty, param_env_def_id) - }); - let blanket_impls = cx.sess().time("get_blanket_impls", || { - BlanketImplFinder::new(cx).get_blanket_impls(ty, param_env_def_id) - }); + let auto_impls = cx + .sess() + .prof + .generic_activity("get_auto_trait_impls") + .run(|| AutoTraitFinder::new(cx).get_auto_trait_impls(ty, param_env_def_id)); + let blanket_impls = cx + .sess() + .prof + .generic_activity("get_blanket_impls") + .run(|| BlanketImplFinder::new(cx).get_blanket_impls(ty, param_env_def_id)); auto_impls.into_iter().chain(blanket_impls) } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 2d58614b13..1478437cef 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::convert::TryFrom; use std::ffi::OsStr; use std::fmt; @@ -16,7 +16,7 @@ use rustc_session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, E use rustc_session::getopts; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; -use rustc_span::edition::{Edition, DEFAULT_EDITION}; +use rustc_span::edition::Edition; use rustc_target::spec::TargetTriple; use crate::core::new_handler; @@ -35,12 +35,15 @@ crate enum OutputFormat { Html, } +impl Default for OutputFormat { + fn default() -> OutputFormat { + OutputFormat::Html + } +} + impl OutputFormat { crate fn is_json(&self) -> bool { - match self { - OutputFormat::Json => true, - _ => false, - } + matches!(self, OutputFormat::Json) } } @@ -106,6 +109,8 @@ crate struct Options { crate should_test: bool, /// List of arguments to pass to the test harness, if running tests. crate test_args: Vec, + /// The working directory in which to run tests. + crate test_run_directory: Option, /// Optional path to persist the doctest executables to, defaults to a /// temporary directory if not set. crate persist_doctests: Option, @@ -119,7 +124,7 @@ crate struct Options { crate enable_per_target_ignores: bool, /// The path to a rustc-like binary to build tests with. If not set, we - /// default to loading from $sysroot/bin/rustc. + /// default to loading from `$sysroot/bin/rustc`. crate test_builder: Option, // Options that affect the documentation process @@ -143,8 +148,10 @@ crate struct Options { crate crate_version: Option, /// Collected options specific to outputting final pages. crate render_options: RenderOptions, - /// Output format rendering (used only for "show-coverage" option for the moment) - crate output_format: Option, + /// The format that we output when rendering. + /// + /// Currently used only for the `--show-coverage` option. + crate output_format: OutputFormat, /// If this option is set to `true`, rustdoc will only run checks and not generate /// documentation. crate run_check: bool, @@ -178,6 +185,7 @@ impl fmt::Debug for Options { .field("lint_cap", &self.lint_cap) .field("should_test", &self.should_test) .field("test_args", &self.test_args) + .field("test_run_directory", &self.test_run_directory) .field("persist_doctests", &self.persist_doctests) .field("default_passes", &self.default_passes) .field("manual_passes", &self.manual_passes) @@ -222,7 +230,7 @@ crate struct RenderOptions { crate extern_html_root_urls: BTreeMap, /// A map of the default settings (values are as for DOM storage API). Keys should lack the /// `rustdoc-` prefix. - crate default_settings: HashMap, + crate default_settings: FxHashMap, /// If present, suffix added to CSS/JavaScript files when referencing them in generated pages. crate resource_suffix: String, /// Whether to run the static CSS/JavaScript through a minifier when outputting them. `true` by @@ -261,7 +269,7 @@ crate struct RenderOptions { } /// Temporary storage for data obtained during `RustdocVisitor::clean()`. -/// Later on moved into `CACHE_KEY`. +/// Later on moved into `cache`. #[derive(Default, Clone)] crate struct RenderInfo { crate inlined: FxHashSet, @@ -271,7 +279,7 @@ crate struct RenderInfo { crate deref_trait_did: Option, crate deref_mut_trait_did: Option, crate owned_box_did: Option, - crate output_format: Option, + crate output_format: OutputFormat, } impl Options { @@ -461,20 +469,10 @@ impl Options { } } - let edition = if let Some(e) = matches.opt_str("edition") { - match e.parse() { - Ok(e) => e, - Err(_) => { - diag.struct_err("could not parse edition").emit(); - return Err(1); - } - } - } else { - DEFAULT_EDITION - }; + let edition = config::parse_crate_edition(&matches); let mut id_map = html::markdown::IdMap::new(); - id_map.populate(html::render::initial_ids()); + id_map.populate(&html::render::INITIAL_IDS); let external_html = match ExternalHtml::load( &matches.opt_strs("html-in-header"), &matches.opt_strs("html-before-content"), @@ -537,28 +535,28 @@ impl Options { let output_format = match matches.opt_str("output-format") { Some(s) => match OutputFormat::try_from(s.as_str()) { - Ok(o) => { - if o.is_json() + Ok(out_fmt) => { + if out_fmt.is_json() && !(show_coverage || nightly_options::match_is_nightly_build(matches)) { diag.struct_err("json output format isn't supported for doc generation") .emit(); return Err(1); - } else if !o.is_json() && show_coverage { + } else if !out_fmt.is_json() && show_coverage { diag.struct_err( "html output format isn't supported for the --show-coverage option", ) .emit(); return Err(1); } - Some(o) + out_fmt } Err(e) => { diag.struct_err(&e).emit(); return Err(1); } }, - None => None, + None => OutputFormat::default(), }; let crate_name = matches.opt_str("crate-name"); let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); @@ -575,6 +573,7 @@ impl Options { let enable_index_page = matches.opt_present("enable-index-page") || index_page.is_some(); let static_root_path = matches.opt_str("static-root-path"); let generate_search_filter = !matches.opt_present("disable-per-crate-search"); + let test_run_directory = matches.opt_str("test-run-directory").map(PathBuf::from); let persist_doctests = matches.opt_str("persist-doctests").map(PathBuf::from); let test_builder = matches.opt_str("test-builder").map(PathBuf::from); let codegen_options_strs = matches.opt_strs("C"); @@ -616,6 +615,7 @@ impl Options { display_warnings, show_coverage, crate_version, + test_run_directory, persist_doctests, runtool, runtool_args, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 7e85342ac7..a20e9dec33 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -24,14 +24,18 @@ use rustc_span::source_map; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; -use std::cell::{Cell, RefCell}; use std::mem; use std::rc::Rc; +use std::{ + cell::{Cell, RefCell}, + collections::hash_map::Entry, +}; use crate::clean; -use crate::clean::{AttributesExt, MAX_DEF_ID}; +use crate::clean::{AttributesExt, MAX_DEF_IDX}; use crate::config::{Options as RustdocOptions, RenderOptions}; use crate::config::{OutputFormat, RenderInfo}; +use crate::formats::cache::Cache; use crate::passes::{self, Condition::*, ConditionalPass}; crate use rustc_session::config::{DebuggingOptions, Input, Options}; @@ -45,9 +49,9 @@ crate struct DocContext<'tcx> { /// /// Most of this logic is copied from rustc_lint::late. crate param_env: Cell>, - /// Later on moved into `CACHE_KEY` + /// Later on moved into `cache` crate renderinfo: RefCell, - /// Later on moved through `clean::Crate` into `CACHE_KEY` + /// Later on moved through `clean::Crate` into `cache` crate external_traits: Rc>>, /// Used while populating `external_traits` to ensure we don't process the same trait twice at /// the same time. @@ -62,8 +66,7 @@ crate struct DocContext<'tcx> { crate ct_substs: RefCell>, /// Table synthetic type parameter for `impl Trait` in argument position -> bounds crate impl_trait_bounds: RefCell>>, - crate fake_def_ids: RefCell>, - crate all_fake_def_ids: RefCell>, + crate fake_def_ids: RefCell>, /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`. // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set. crate generated_synthetics: RefCell, DefId)>>, @@ -75,6 +78,8 @@ crate struct DocContext<'tcx> { /// See `collect_intra_doc_links::traits_implemented_by` for more details. /// `map>` crate module_trait_cache: RefCell>>, + /// Fake empty cache used when cache is required as parameter. + crate cache: Cache, } impl<'tcx> DocContext<'tcx> { @@ -120,46 +125,53 @@ impl<'tcx> DocContext<'tcx> { r } - // This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly - // refactoring either librustdoc or librustc_middle. In particular, allowing new DefIds to be - // registered after the AST is constructed would require storing the defid mapping in a - // RefCell, decreasing the performance for normal compilation for very little gain. - // - // Instead, we construct 'fake' def ids, which start immediately after the last DefId. - // In the Debug impl for clean::Item, we explicitly check for fake - // def ids, as we'll end up with a panic if we use the DefId Debug impl for fake DefIds + /// Create a new "fake" [`DefId`]. + /// + /// This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly + /// refactoring either rustdoc or [`rustc_middle`]. In particular, allowing new [`DefId`]s + /// to be registered after the AST is constructed would require storing the [`DefId`] mapping + /// in a [`RefCell`], decreasing the performance for normal compilation for very little gain. + /// + /// Instead, we construct "fake" [`DefId`]s, which start immediately after the last `DefId`. + /// In the [`Debug`] impl for [`clean::Item`], we explicitly check for fake `DefId`s, + /// as we'll end up with a panic if we use the `DefId` `Debug` impl for fake `DefId`s. + /// + /// [`RefCell`]: std::cell::RefCell + /// [`Debug`]: std::fmt::Debug + /// [`clean::Item`]: crate::clean::types::Item crate fn next_def_id(&self, crate_num: CrateNum) -> DefId { - let start_def_id = { - let num_def_ids = if crate_num == LOCAL_CRATE { - self.tcx.hir().definitions().def_path_table().num_def_ids() - } else { - self.enter_resolver(|r| r.cstore().num_def_ids(crate_num)) - }; - - DefId { krate: crate_num, index: DefIndex::from_usize(num_def_ids) } - }; - let mut fake_ids = self.fake_def_ids.borrow_mut(); - let def_id = *fake_ids.entry(crate_num).or_insert(start_def_id); - fake_ids.insert( - crate_num, - DefId { krate: crate_num, index: DefIndex::from(def_id.index.index() + 1) }, - ); - - MAX_DEF_ID.with(|m| { - m.borrow_mut().entry(def_id.krate).or_insert(start_def_id); - }); - - self.all_fake_def_ids.borrow_mut().insert(def_id); + let def_index = match fake_ids.entry(crate_num) { + Entry::Vacant(e) => { + let num_def_idx = { + let num_def_idx = if crate_num == LOCAL_CRATE { + self.tcx.hir().definitions().def_path_table().num_def_ids() + } else { + self.enter_resolver(|r| r.cstore().num_def_ids(crate_num)) + }; + + DefIndex::from_usize(num_def_idx) + }; + + MAX_DEF_IDX.with(|m| { + m.borrow_mut().insert(crate_num, num_def_idx); + }); + e.insert(num_def_idx) + } + Entry::Occupied(e) => e.into_mut(), + }; + *def_index = DefIndex::from(*def_index + 1); - def_id + DefId { krate: crate_num, index: *def_index } } /// Like `hir().local_def_id_to_hir_id()`, but skips calling it on fake DefIds. /// (This avoids a slice-index-out-of-bounds panic.) crate fn as_local_hir_id(&self, def_id: DefId) -> Option { - if self.all_fake_def_ids.borrow().contains(&def_id) { + if MAX_DEF_IDX.with(|m| { + m.borrow().get(&def_id.krate).map(|&idx| idx <= def_id.index).unwrap_or(false) + }) { None } else { def_id.as_local().map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)) @@ -423,21 +435,20 @@ crate fn create_resolver<'a>( // Before we actually clone it, let's force all the extern'd crates to // actually be loaded, just in case they're only referred to inside - // intra-doc-links + // intra-doc links resolver.borrow_mut().access(|resolver| { sess.time("load_extern_crates", || { for extern_name in &extern_names { debug!("loading extern crate {}", extern_name); - resolver + if let Err(()) = resolver .resolve_str_path_error( DUMMY_SP, extern_name, TypeNS, LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(), - ) - .unwrap_or_else(|()| { - panic!("Unable to resolve external crate {}", extern_name) - }); + ) { + warn!("unable to resolve external crate {} (do you have an unused `--extern` crate?)", extern_name) + } } }); }); @@ -452,7 +463,7 @@ crate fn run_global_ctxt( mut default_passes: passes::DefaultPassOption, mut manual_passes: Vec, render_options: RenderOptions, - output_format: Option, + output_format: OutputFormat, ) -> (clean::Crate, RenderInfo, RenderOptions) { // Certain queries assume that some checks were run elsewhere // (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425), @@ -509,7 +520,6 @@ crate fn run_global_ctxt( ct_substs: Default::default(), impl_trait_bounds: Default::default(), fake_def_ids: Default::default(), - all_fake_def_ids: Default::default(), generated_synthetics: Default::default(), auto_traits: tcx .all_traits(LOCAL_CRATE) @@ -519,13 +529,14 @@ crate fn run_global_ctxt( .collect(), render_options, module_trait_cache: RefCell::new(FxHashMap::default()), + cache: Cache::default(), }; debug!("crate: {:?}", tcx.hir().krate()); let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt)); if let Some(ref m) = krate.module { - if let None | Some("") = m.doc_value() { + if m.doc_value().map(|d| d.is_empty()).unwrap_or(true) { let help = "The following guide may be of use:\n\ https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html"; tcx.struct_lint_node( @@ -623,6 +634,9 @@ crate fn run_global_ctxt( ctxt.sess().abort_if_errors(); + // The main crate doc comments are always collapsed. + krate.collapsed = true; + (krate, ctxt.renderinfo.into_inner(), ctxt.render_options) } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 7313c761ea..eecfd337cd 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,4 +1,5 @@ use rustc_ast as ast; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::{ColorConfig, ErrorReported}; use rustc_hir as hir; @@ -16,7 +17,6 @@ use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use rustc_target::spec::TargetTriple; use tempfile::Builder as TempFileBuilder; -use std::collections::HashMap; use std::env; use std::io::{self, Write}; use std::panic; @@ -296,7 +296,12 @@ fn run_test( } }); if let ErrorOutputType::HumanReadable(kind) = options.error_format { - let (_, color_config) = kind.unzip(); + let (short, color_config) = kind.unzip(); + + if short { + compiler.arg("--error-format").arg("short"); + } + match color_config { ColorConfig::Never => { compiler.arg("--color").arg("never"); @@ -365,6 +370,9 @@ fn run_test( } else { cmd = Command::new(output_file); } + if let Some(run_directory) = options.test_run_directory { + cmd.current_dir(run_directory); + } match cmd.output() { Err(e) => return Err(TestFailure::ExecutionError(e)), @@ -423,6 +431,7 @@ crate fn make_test( use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::Handler; use rustc_parse::maybe_new_parser_from_source_str; + use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; use rustc_span::source_map::FilePathMapping; @@ -459,7 +468,7 @@ crate fn make_test( }; loop { - match parser.parse_item() { + match parser.parse_item(ForceCollect::No) { Ok(Some(item)) => { if !found_main { if let ast::ItemKind::Fn(..) = item.kind { @@ -500,6 +509,12 @@ crate fn make_test( } } + // Reset errors so that they won't be reported as compiler bugs when dropping the + // handler. Any errors in the tests will be reported when the test file is compiled, + // Note that we still need to cancel the errors above otherwise `DiagnosticBuilder` + // will panic on drop. + sess.span_diagnostic.reset_err_count(); + (found_main, found_extern_crate, found_macro) }) }); @@ -558,12 +573,12 @@ crate fn make_test( "fn main() {{ {}fn {}() -> Result<(), impl core::fmt::Debug> {{\n", inner_attr, inner_fn_name ), - format!("\n}}; {}().unwrap() }}", inner_fn_name), + format!("\n}} {}().unwrap() }}", inner_fn_name), ) } else if test_id.is_some() { ( format!("fn main() {{ {}fn {}() {{\n", inner_attr, inner_fn_name), - format!("\n}}; {}() }}", inner_fn_name), + format!("\n}} {}() }}", inner_fn_name), ) } else { ("fn main() {\n".into(), "\n}".into()) @@ -636,15 +651,15 @@ fn partition_source(s: &str) -> (String, String, String) { match state { PartitionState::Attrs => { before.push_str(line); - before.push_str("\n"); + before.push('\n'); } PartitionState::Crates => { crates.push_str(line); - crates.push_str("\n"); + crates.push('\n'); } PartitionState::Other => { after.push_str(line); - after.push_str("\n"); + after.push('\n'); } } } @@ -697,7 +712,7 @@ crate struct Collector { position: Span, source_map: Option>, filename: Option, - visited_tests: HashMap<(String, usize), usize>, + visited_tests: FxHashMap<(String, usize), usize>, } impl Collector { @@ -721,7 +736,7 @@ impl Collector { position: DUMMY_SP, source_map, filename, - visited_tests: HashMap::new(), + visited_tests: FxHashMap::default(), } } @@ -987,7 +1002,6 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { self.collector.names.push(name); } - attrs.collapse_doc_comments(); attrs.unindent_doc_comments(); // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with // anything else, this will combine them for us. @@ -1004,7 +1018,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { self.codes, self.collector.enable_per_target_ignores, Some(&crate::html::markdown::ExtraInfo::new( - &self.tcx, + self.tcx, hir_id, span_of_attrs(&attrs).unwrap_or(sp), )), @@ -1027,8 +1041,8 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> } fn visit_item(&mut self, item: &'hir hir::Item<'_>) { - let name = if let hir::ItemKind::Impl { ref self_ty, .. } = item.kind { - rustc_hir_pretty::id_to_string(&self.map, self_ty.hir_id) + let name = if let hir::ItemKind::Impl(impl_) = &item.kind { + rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id) } else { item.ident.to_string() }; diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 1aea85e997..465b2b1d69 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -292,7 +292,7 @@ use std::io; let mut input = String::new(); io::stdin().read_line(&mut input)?; Ok::<(), io:Error>(()) -}; _inner().unwrap() }" +} _inner().unwrap() }" .to_string(); let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None); assert_eq!((output, len), (expected, 2)); @@ -306,7 +306,7 @@ fn make_test_named_wrapper() { let expected = "#![allow(unused)] fn main() { #[allow(non_snake_case)] fn _doctest_main__some_unique_name() { assert_eq!(2+2, 4); -}; _doctest_main__some_unique_name() }" +} _doctest_main__some_unique_name() }" .to_string(); let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, Some("_some_unique_name")); diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index bc9f1cf880..645b2bb193 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -1,8 +1,5 @@ //! This module is used to store stuff from Rust's AST in a more convenient //! manner (and with prettier names) before cleaning. -crate use self::StructType::*; - -use rustc_ast as ast; use rustc_span::{self, Span, Symbol}; use rustc_hir as hir; @@ -11,7 +8,6 @@ crate struct Module<'hir> { crate name: Option, crate where_outer: Span, crate where_inner: Span, - crate imports: Vec>, crate mods: Vec>, crate id: hir::HirId, // (item, renamed) @@ -28,7 +24,6 @@ impl Module<'hir> { id: hir::CRATE_HIR_ID, where_outer: rustc_span::DUMMY_SP, where_inner: rustc_span::DUMMY_SP, - imports: Vec::new(), mods: Vec::new(), items: Vec::new(), foreigns: Vec::new(), @@ -37,38 +32,3 @@ impl Module<'hir> { } } } - -#[derive(Debug, Clone, Copy)] -crate enum StructType { - /// A braced struct - Plain, - /// A tuple struct - Tuple, - /// A unit struct - Unit, -} - -crate struct Variant<'hir> { - crate name: Symbol, - crate id: hir::HirId, - crate def: &'hir hir::VariantData<'hir>, -} - -#[derive(Debug)] -crate struct Import<'hir> { - crate name: Symbol, - crate id: hir::HirId, - crate vis: &'hir hir::Visibility<'hir>, - crate attrs: &'hir [ast::Attribute], - crate path: &'hir hir::Path<'hir>, - crate glob: bool, - crate span: Span, -} - -crate fn struct_type_from_def(vdata: &hir::VariantData<'_>) -> StructType { - match *vdata { - hir::VariantData::Struct(..) => Plain, - hir::VariantData::Tuple(..) => Tuple, - hir::VariantData::Unit(..) => Unit, - } -} diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 285fabdc37..b2773a29e2 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -3,12 +3,12 @@ use crate::clean::*; crate struct StripItem(pub Item); impl StripItem { - crate fn strip(self) -> Option { + crate fn strip(self) -> Item { match self.0 { - Item { kind: StrippedItem(..), .. } => Some(self.0), + Item { kind: box StrippedItem(..), .. } => self.0, mut i => { - i.kind = StrippedItem(box i.kind); - Some(i) + i.kind = box StrippedItem(i.kind); + i } } } @@ -55,13 +55,13 @@ crate trait DocFolder: Sized { } VariantItem(i) => { let i2 = i.clone(); // this clone is small - match i.kind { - VariantKind::Struct(mut j) => { + match i { + Variant::Struct(mut j) => { let num_fields = j.fields.len(); j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); j.fields_stripped |= num_fields != j.fields.len() || j.fields.iter().any(|f| f.is_stripped()); - VariantItem(Variant { kind: VariantKind::Struct(j), ..i2 }) + VariantItem(Variant::Struct(j)) } _ => VariantItem(i2), } @@ -72,9 +72,9 @@ crate trait DocFolder: Sized { /// don't override! fn fold_item_recur(&mut self, mut item: Item) -> Item { - item.kind = match item.kind { + item.kind = box match *item.kind { StrippedItem(box i) => StrippedItem(box self.fold_inner_recur(i)), - _ => self.fold_inner_recur(item.kind), + _ => self.fold_inner_recur(*item.kind), }; item } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 77a3e9fa95..c506f5a37b 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,8 +1,6 @@ -use std::cell::RefCell; use std::collections::BTreeMap; use std::mem; use std::path::{Path, PathBuf}; -use std::sync::Arc; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; @@ -19,8 +17,6 @@ use crate::html::markdown::short_markdown_summary; use crate::html::render::cache::{extern_location, get_index_search_type, ExternalLocation}; use crate::html::render::IndexItem; -thread_local!(crate static CACHE_KEY: RefCell> = Default::default()); - /// This cache is used to store information about the [`clean::Crate`] being /// rendered in order to provide more useful documentation. This contains /// information like all implementors of a trait, all traits a type implements, @@ -197,6 +193,7 @@ impl Cache { } cache.stack.push(krate.name.to_string()); + krate = cache.fold_crate(krate); for (trait_did, dids, impl_) in cache.orphan_trait_impls.drain(..) { @@ -219,7 +216,7 @@ impl DocFolder for Cache { // If this is a stripped module, // we don't want it or its children in the search index. - let orig_stripped_mod = match item.kind { + let orig_stripped_mod = match *item.kind { clean::StrippedItem(box clean::ModuleItem(..)) => { mem::replace(&mut self.stripped_mod, true) } @@ -228,7 +225,7 @@ impl DocFolder for Cache { // If the impl is from a masked crate or references something from a // masked crate then remove it completely. - if let clean::ImplItem(ref i) = item.kind { + if let clean::ImplItem(ref i) = *item.kind { if self.masked_crates.contains(&item.def_id.krate) || i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) || i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) @@ -239,12 +236,12 @@ impl DocFolder for Cache { // Propagate a trait method's documentation to all implementors of the // trait. - if let clean::TraitItem(ref t) = item.kind { + if let clean::TraitItem(ref t) = *item.kind { self.traits.entry(item.def_id).or_insert_with(|| t.clone()); } // Collect all the implementors of traits. - if let clean::ImplItem(ref i) = item.kind { + if let clean::ImplItem(ref i) = *item.kind { if let Some(did) = i.trait_.def_id() { if i.blanket_impl.is_none() { self.implementors @@ -257,7 +254,7 @@ impl DocFolder for Cache { // Index this method for searching later on. if let Some(ref s) = item.name { - let (parent, is_inherent_impl_item) = match item.kind { + let (parent, is_inherent_impl_item) = match *item.kind { clean::StrippedItem(..) => ((None, None), false), clean::AssocConstItem(..) | clean::TypedefItem(_, true) if self.parent_is_trait_impl => @@ -316,10 +313,10 @@ impl DocFolder for Cache { path: path.join("::"), desc: item .doc_value() - .map_or_else(|| String::new(), short_markdown_summary), + .map_or_else(String::new, |x| short_markdown_summary(&x.as_str())), parent, parent_idx: None, - search_type: get_index_search_type(&item), + search_type: get_index_search_type(&item, None), }); for alias in item.attrs.get_doc_aliases() { @@ -348,7 +345,7 @@ impl DocFolder for Cache { _ => false, }; - match item.kind { + match *item.kind { clean::StructItem(..) | clean::EnumItem(..) | clean::TypedefItem(..) @@ -387,7 +384,7 @@ impl DocFolder for Cache { // Maintain the parent stack let orig_parent_is_trait_impl = self.parent_is_trait_impl; - let parent_pushed = match item.kind { + let parent_pushed = match *item.kind { clean::TraitItem(..) | clean::EnumItem(..) | clean::ForeignTypeItem @@ -425,38 +422,33 @@ impl DocFolder for Cache { // Once we've recursively found all the generics, hoard off all the // implementations elsewhere. let item = self.fold_item_recur(item); - let ret = if let clean::Item { kind: clean::ImplItem(_), .. } = item { + let ret = if let clean::Item { kind: box clean::ImplItem(ref i), .. } = item { // Figure out the id of this impl. This may map to a // primitive rather than always to a struct/enum. // Note: matching twice to restrict the lifetime of the `i` borrow. let mut dids = FxHashSet::default(); - if let clean::Item { kind: clean::ImplItem(ref i), .. } = item { - match i.for_ { - clean::ResolvedPath { did, .. } - | clean::BorrowedRef { type_: box clean::ResolvedPath { did, .. }, .. } => { - dids.insert(did); - } - ref t => { - let did = t - .primitive_type() - .and_then(|t| self.primitive_locations.get(&t).cloned()); + match i.for_ { + clean::ResolvedPath { did, .. } + | clean::BorrowedRef { type_: box clean::ResolvedPath { did, .. }, .. } => { + dids.insert(did); + } + ref t => { + let did = + t.primitive_type().and_then(|t| self.primitive_locations.get(&t).cloned()); - if let Some(did) = did { - dids.insert(did); - } + if let Some(did) = did { + dids.insert(did); } } + } - if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { - for bound in generics { - if let Some(did) = bound.def_id() { - dids.insert(did); - } + if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { + for bound in generics { + if let Some(did) = bound.def_id() { + dids.insert(did); } } - } else { - unreachable!() - }; + } let impl_item = Impl { impl_item: item }; if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) { for did in dids { @@ -482,7 +474,3 @@ impl DocFolder for Cache { ret } } - -crate fn cache() -> Arc { - CACHE_KEY.with(|c| c.borrow().clone()) -} diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index af512e3746..7922a1ffa0 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -60,7 +60,7 @@ impl Serialize for ItemType { impl<'a> From<&'a clean::Item> for ItemType { fn from(item: &'a clean::Item) -> ItemType { - let kind = match item.kind { + let kind = match *item.kind { clean::StrippedItem(box ref item) => item, ref kind => kind, }; @@ -119,6 +119,7 @@ impl From for ItemType { clean::TypeKind::Attr => ItemType::ProcAttribute, clean::TypeKind::Derive => ItemType::ProcDerive, clean::TypeKind::TraitAlias => ItemType::TraitAlias, + clean::TypeKind::Primitive => ItemType::Primitive, } } } @@ -158,6 +159,6 @@ impl ItemType { impl fmt::Display for ItemType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) + f.write_str(self.as_str()) } } diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs index 55fd4948f4..1ce6572bbe 100644 --- a/src/librustdoc/formats/mod.rs +++ b/src/librustdoc/formats/mod.rs @@ -8,6 +8,7 @@ use rustc_span::def_id::DefId; use crate::clean; use crate::clean::types::GetDefId; +use crate::formats::cache::Cache; /// Specifies whether rendering directly implemented trait items or ones from a certain Deref /// impl. @@ -32,7 +33,7 @@ crate struct Impl { impl Impl { crate fn inner_impl(&self) -> &clean::Impl { - match self.impl_item.kind { + match *self.impl_item.kind { clean::ImplItem(ref impl_) => impl_, _ => panic!("non-impl item found in impl"), } @@ -41,4 +42,8 @@ impl Impl { crate fn trait_did(&self) -> Option { self.inner_impl().trait_.def_id() } + + crate fn trait_did_full(&self, cache: &Cache) -> Option { + self.inner_impl().trait_.def_id_full(cache) + } } diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index fbbd3c1ccf..6ecc4695dc 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -1,17 +1,18 @@ -use std::sync::Arc; - -use rustc_middle::ty; +use rustc_middle::ty::TyCtxt; use rustc_span::edition::Edition; use crate::clean; use crate::config::{RenderInfo, RenderOptions}; use crate::error::Error; -use crate::formats::cache::{Cache, CACHE_KEY}; +use crate::formats::cache::Cache; /// Allows for different backends to rustdoc to be used with the `run_format()` function. Each /// backend renderer has hooks for initialization, documenting an item, entering and exiting a /// module, and cleanup/finalizing output. crate trait FormatRenderer<'tcx>: Clone { + /// Gives a description of the renderer. Used for performance profiling. + fn descr() -> &'static str; + /// Sets up any state required for the renderer. When this is called the cache has already been /// populated. fn init( @@ -19,29 +20,29 @@ crate trait FormatRenderer<'tcx>: Clone { options: RenderOptions, render_info: RenderInfo, edition: Edition, - cache: &mut Cache, - tcx: ty::TyCtxt<'tcx>, + cache: Cache, + tcx: TyCtxt<'tcx>, ) -> Result<(Self, clean::Crate), Error>; /// Renders a single non-module item. This means no recursive sub-item rendering is required. - fn item(&mut self, item: clean::Item, cache: &Cache) -> Result<(), Error>; + fn item(&mut self, item: clean::Item) -> Result<(), Error>; /// Renders a module (should not handle recursing into children). - fn mod_item_in( - &mut self, - item: &clean::Item, - item_name: &str, - cache: &Cache, - ) -> Result<(), Error>; + fn mod_item_in(&mut self, item: &clean::Item, item_name: &str) -> Result<(), Error>; /// Runs after recursively rendering all sub-items of a module. fn mod_item_out(&mut self, item_name: &str) -> Result<(), Error>; /// Post processing hook for cleanup and dumping output to files. - fn after_krate(&mut self, krate: &clean::Crate, cache: &Cache) -> Result<(), Error>; + /// + /// A handler is available if the renderer wants to report errors. + fn after_krate( + &mut self, + krate: &clean::Crate, + diag: &rustc_errors::Handler, + ) -> Result<(), Error>; - /// Called after everything else to write out errors. - fn after_run(&mut self, diag: &rustc_errors::Handler) -> Result<(), Error>; + fn cache(&self) -> &Cache; } /// Main method for rendering a crate. @@ -51,23 +52,22 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>( render_info: RenderInfo, diag: &rustc_errors::Handler, edition: Edition, - tcx: ty::TyCtxt<'tcx>, + tcx: TyCtxt<'tcx>, ) -> Result<(), Error> { - let (krate, mut cache) = Cache::from_krate( - render_info.clone(), - options.document_private, - &options.extern_html_root_urls, - &options.output, - krate, - ); - - let (mut format_renderer, mut krate) = - T::init(krate, options, render_info, edition, &mut cache, tcx)?; - - let cache = Arc::new(cache); - // Freeze the cache now that the index has been built. Put an Arc into TLS for future - // parallelization opportunities - CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone()); + let (krate, cache) = tcx.sess.time("create_format_cache", || { + Cache::from_krate( + render_info.clone(), + options.document_private, + &options.extern_html_root_urls, + &options.output, + krate, + ) + }); + let prof = &tcx.sess.prof; + + let (mut format_renderer, mut krate) = prof + .extra_verbose_generic_activity("create_renderer", T::descr()) + .run(|| T::init(krate, options, render_info, edition, cache, tcx))?; let mut item = match krate.module.take() { Some(i) => i, @@ -79,6 +79,7 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>( // Render the crate documentation let mut work = vec![(format_renderer.clone(), item)]; + let unknown = rustc_span::Symbol::intern(""); while let Some((mut cx, item)) = work.pop() { if item.is_mod() { // modules are special because they add a namespace. We also need to @@ -87,9 +88,10 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>( if name.is_empty() { panic!("Unexpected module with empty name"); } + let _timer = prof.generic_activity_with_arg("render_mod_item", name.as_str()); - cx.mod_item_in(&item, &name, &cache)?; - let module = match item.kind { + cx.mod_item_in(&item, &name)?; + let module = match *item.kind { clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m, _ => unreachable!(), }; @@ -100,10 +102,10 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>( cx.mod_item_out(&name)?; } else if item.name.is_some() { - cx.item(item, &cache)?; + prof.generic_activity_with_arg("render_item", &*item.name.unwrap_or(unknown).as_str()) + .run(|| cx.item(item))?; } } - - format_renderer.after_krate(&krate, &cache)?; - format_renderer.after_run(diag) + prof.extra_verbose_generic_activity("renderer_after_krate", T::descr()) + .run(|| format_renderer.after_krate(&krate, diag)) } diff --git a/src/librustdoc/html/escape.rs b/src/librustdoc/html/escape.rs index 60c1955198..ce44722a53 100644 --- a/src/librustdoc/html/escape.rs +++ b/src/librustdoc/html/escape.rs @@ -16,23 +16,20 @@ impl<'a> fmt::Display for Escape<'a> { let Escape(s) = *self; let pile_o_bits = s; let mut last = 0; - for (i, ch) in s.bytes().enumerate() { - match ch as char { - '<' | '>' | '&' | '\'' | '"' => { - fmt.write_str(&pile_o_bits[last..i])?; - let s = match ch as char { - '>' => ">", - '<' => "<", - '&' => "&", - '\'' => "'", - '"' => """, - _ => unreachable!(), - }; - fmt.write_str(s)?; - last = i + 1; - } - _ => {} - } + for (i, ch) in s.char_indices() { + let s = match ch { + '>' => ">", + '<' => "<", + '&' => "&", + '\'' => "'", + '"' => """, + _ => continue, + }; + fmt.write_str(&pile_o_bits[last..i])?; + fmt.write_str(s)?; + // NOTE: we only expect single byte characters here - which is fine as long as we + // only match single byte characters + last = i + 1; } if last < s.len() { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 9b2fb8582f..d795196122 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -16,7 +16,7 @@ use rustc_span::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_target::spec::abi::Abi; use crate::clean::{self, utils::find_nearest_parent_module, PrimitiveType}; -use crate::formats::cache::cache; +use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::render::cache::ExternalLocation; @@ -152,24 +152,27 @@ fn comma_sep(items: impl Iterator) -> impl fmt::Displ }) } -crate fn print_generic_bounds(bounds: &[clean::GenericBound]) -> impl fmt::Display + '_ { +crate fn print_generic_bounds<'a>( + bounds: &'a [clean::GenericBound], + cache: &'a Cache, +) -> impl fmt::Display + 'a { display_fn(move |f| { let mut bounds_dup = FxHashSet::default(); for (i, bound) in - bounds.iter().filter(|b| bounds_dup.insert(b.print().to_string())).enumerate() + bounds.iter().filter(|b| bounds_dup.insert(b.print(cache).to_string())).enumerate() { if i > 0 { f.write_str(" + ")?; } - fmt::Display::fmt(&bound.print(), f)?; + fmt::Display::fmt(&bound.print(cache), f)?; } Ok(()) }) } impl clean::GenericParamDef { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| match self.kind { clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name), clean::GenericParamDefKind::Type { ref bounds, ref default, .. } => { @@ -177,17 +180,17 @@ impl clean::GenericParamDef { if !bounds.is_empty() { if f.alternate() { - write!(f, ": {:#}", print_generic_bounds(bounds))?; + write!(f, ": {:#}", print_generic_bounds(bounds, cache))?; } else { - write!(f, ": {}", print_generic_bounds(bounds))?; + write!(f, ": {}", print_generic_bounds(bounds, cache))?; } } if let Some(ref ty) = default { if f.alternate() { - write!(f, " = {:#}", ty.print())?; + write!(f, " = {:#}", ty.print(cache))?; } else { - write!(f, " = {}", ty.print())?; + write!(f, " = {}", ty.print(cache))?; } } @@ -195,9 +198,9 @@ impl clean::GenericParamDef { } clean::GenericParamDefKind::Const { ref ty, .. } => { if f.alternate() { - write!(f, "const {}: {:#}", self.name, ty.print()) + write!(f, "const {}: {:#}", self.name, ty.print(cache)) } else { - write!(f, "const {}: {}", self.name, ty.print()) + write!(f, "const {}: {}", self.name, ty.print(cache)) } } }) @@ -205,7 +208,7 @@ impl clean::GenericParamDef { } impl clean::Generics { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| { let real_params = self.params.iter().filter(|p| !p.is_synthetic_type_param()).collect::>(); @@ -213,98 +216,108 @@ impl clean::Generics { return Ok(()); } if f.alternate() { - write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print()))) + write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cache)))) } else { - write!(f, "<{}>", comma_sep(real_params.iter().map(|g| g.print()))) + write!(f, "<{}>", comma_sep(real_params.iter().map(|g| g.print(cache)))) } }) } } -impl<'a> fmt::Display for WhereClause<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let &WhereClause { gens, indent, end_newline } = self; - if gens.where_predicates.is_empty() { - return Ok(()); - } - let mut clause = String::new(); - if f.alternate() { - clause.push_str(" where"); - } else { - if end_newline { - clause.push_str(" where"); - } else { - clause.push_str(" where"); +impl<'a> WhereClause<'a> { + crate fn print<'b>(&'b self, cache: &'b Cache) -> impl fmt::Display + 'b { + display_fn(move |f| { + let &WhereClause { gens, indent, end_newline } = self; + if gens.where_predicates.is_empty() { + return Ok(()); } - } - for (i, pred) in gens.where_predicates.iter().enumerate() { + let mut clause = String::new(); if f.alternate() { - clause.push(' '); + clause.push_str(" where"); } else { - clause.push_str("
"); + if end_newline { + clause.push_str(" where"); + } else { + clause.push_str(" where"); + } } + for (i, pred) in gens.where_predicates.iter().enumerate() { + if f.alternate() { + clause.push(' '); + } else { + clause.push_str("
"); + } - match pred { - &clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => { - let bounds = bounds; - if f.alternate() { - clause.push_str(&format!( - "{:#}: {:#}", - ty.print(), - print_generic_bounds(bounds) - )); - } else { + match pred { + clean::WherePredicate::BoundPredicate { ty, bounds } => { + let bounds = bounds; + if f.alternate() { + clause.push_str(&format!( + "{:#}: {:#}", + ty.print(cache), + print_generic_bounds(bounds, cache) + )); + } else { + clause.push_str(&format!( + "{}: {}", + ty.print(cache), + print_generic_bounds(bounds, cache) + )); + } + } + clean::WherePredicate::RegionPredicate { lifetime, bounds } => { clause.push_str(&format!( "{}: {}", - ty.print(), - print_generic_bounds(bounds) + lifetime.print(), + bounds + .iter() + .map(|b| b.print(cache).to_string()) + .collect::>() + .join(" + ") )); } - } - &clean::WherePredicate::RegionPredicate { ref lifetime, ref bounds } => { - clause.push_str(&format!( - "{}: {}", - lifetime.print(), - bounds - .iter() - .map(|b| b.print().to_string()) - .collect::>() - .join(" + ") - )); - } - &clean::WherePredicate::EqPredicate { ref lhs, ref rhs } => { - if f.alternate() { - clause.push_str(&format!("{:#} == {:#}", lhs.print(), rhs.print())); - } else { - clause.push_str(&format!("{} == {}", lhs.print(), rhs.print())); + clean::WherePredicate::EqPredicate { lhs, rhs } => { + if f.alternate() { + clause.push_str(&format!( + "{:#} == {:#}", + lhs.print(cache), + rhs.print(cache) + )); + } else { + clause.push_str(&format!( + "{} == {}", + lhs.print(cache), + rhs.print(cache) + )); + } } } - } - if i < gens.where_predicates.len() - 1 || end_newline { - clause.push(','); + if i < gens.where_predicates.len() - 1 || end_newline { + clause.push(','); + } } - } - if end_newline { - // add a space so stripping
tags and breaking spaces still renders properly - if f.alternate() { - clause.push(' '); - } else { - clause.push_str(" "); + if end_newline { + // add a space so stripping
tags and breaking spaces still renders properly + if f.alternate() { + clause.push(' '); + } else { + clause.push_str(" "); + } } - } - if !f.alternate() { - clause.push_str("
"); - let padding = " ".repeat(indent + 4); - clause = clause.replace("
", &format!("
{}", padding)); - clause.insert_str(0, &" ".repeat(indent.saturating_sub(1))); - if !end_newline { - clause.insert_str(0, "
"); + if !f.alternate() { + clause.push_str("
"); + let padding = " ".repeat(indent + 4); + clause = clause.replace("
", &format!("
{}", padding)); + clause.insert_str(0, &" ".repeat(indent.saturating_sub(1))); + if !end_newline { + clause.insert_str(0, "
"); + } } - } - write!(f, "{}", clause) + write!(f, "{}", clause) + }) } } @@ -327,34 +340,34 @@ impl clean::Constant { } impl clean::PolyTrait { - fn print(&self) -> impl fmt::Display + '_ { + fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| { if !self.generic_params.is_empty() { if f.alternate() { write!( f, "for<{:#}> ", - comma_sep(self.generic_params.iter().map(|g| g.print())) + comma_sep(self.generic_params.iter().map(|g| g.print(cache))) )?; } else { write!( f, "for<{}> ", - comma_sep(self.generic_params.iter().map(|g| g.print())) + comma_sep(self.generic_params.iter().map(|g| g.print(cache))) )?; } } if f.alternate() { - write!(f, "{:#}", self.trait_.print()) + write!(f, "{:#}", self.trait_.print(cache)) } else { - write!(f, "{}", self.trait_.print()) + write!(f, "{}", self.trait_.print(cache)) } }) } } impl clean::GenericBound { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| match self { clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), clean::GenericBound::TraitBound(ty, modifier) => { @@ -364,9 +377,9 @@ impl clean::GenericBound { hir::TraitBoundModifier::MaybeConst => "?const", }; if f.alternate() { - write!(f, "{}{:#}", modifier_str, ty.print()) + write!(f, "{}{:#}", modifier_str, ty.print(cache)) } else { - write!(f, "{}{}", modifier_str, ty.print()) + write!(f, "{}{}", modifier_str, ty.print(cache)) } } }) @@ -374,10 +387,10 @@ impl clean::GenericBound { } impl clean::GenericArgs { - fn print(&self) -> impl fmt::Display + '_ { + fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| { - match *self { - clean::GenericArgs::AngleBracketed { ref args, ref bindings } => { + match self { + clean::GenericArgs::AngleBracketed { args, bindings } => { if !args.is_empty() || !bindings.is_empty() { if f.alternate() { f.write_str("<")?; @@ -391,9 +404,9 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", arg.print())?; + write!(f, "{:#}", arg.print(cache))?; } else { - write!(f, "{}", arg.print())?; + write!(f, "{}", arg.print(cache))?; } } for binding in bindings { @@ -402,9 +415,9 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", binding.print())?; + write!(f, "{:#}", binding.print(cache))?; } else { - write!(f, "{}", binding.print())?; + write!(f, "{}", binding.print(cache))?; } } if f.alternate() { @@ -414,7 +427,7 @@ impl clean::GenericArgs { } } } - clean::GenericArgs::Parenthesized { ref inputs, ref output } => { + clean::GenericArgs::Parenthesized { inputs, output } => { f.write_str("(")?; let mut comma = false; for ty in inputs { @@ -423,17 +436,17 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", ty.print())?; + write!(f, "{:#}", ty.print(cache))?; } else { - write!(f, "{}", ty.print())?; + write!(f, "{}", ty.print(cache))?; } } f.write_str(")")?; if let Some(ref ty) = *output { if f.alternate() { - write!(f, " -> {:#}", ty.print())?; + write!(f, " -> {:#}", ty.print(cache))?; } else { - write!(f, " -> {}", ty.print())?; + write!(f, " -> {}", ty.print(cache))?; } } } @@ -444,19 +457,19 @@ impl clean::GenericArgs { } impl clean::PathSegment { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| { if f.alternate() { - write!(f, "{}{:#}", self.name, self.args.print()) + write!(f, "{}{:#}", self.name, self.args.print(cache)) } else { - write!(f, "{}{}", self.name, self.args.print()) + write!(f, "{}{}", self.name, self.args.print(cache)) } }) } } impl clean::Path { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| { if self.global { f.write_str("::")? @@ -467,9 +480,9 @@ impl clean::Path { f.write_str("::")? } if f.alternate() { - write!(f, "{:#}", seg.print())?; + write!(f, "{:#}", seg.print(cache))?; } else { - write!(f, "{}", seg.print())?; + write!(f, "{}", seg.print(cache))?; } } Ok(()) @@ -477,8 +490,7 @@ impl clean::Path { } } -crate fn href(did: DefId) -> Option<(String, ItemType, Vec)> { - let cache = cache(); +crate fn href(did: DefId, cache: &Cache) -> Option<(String, ItemType, Vec)> { if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private { return None; } @@ -501,7 +513,7 @@ crate fn href(did: DefId) -> Option<(String, ItemType, Vec)> { }; for component in &fqp[..fqp.len() - 1] { url.push_str(component); - url.push_str("/"); + url.push('/'); } match shortty { ItemType::Module => { @@ -510,7 +522,7 @@ crate fn href(did: DefId) -> Option<(String, ItemType, Vec)> { } _ => { url.push_str(shortty.as_str()); - url.push_str("."); + url.push('.'); url.push_str(fqp.last().unwrap()); url.push_str(".html"); } @@ -526,6 +538,7 @@ fn resolved_path( path: &clean::Path, print_all: bool, use_absolute: bool, + cache: &Cache, ) -> fmt::Result { let last = path.segments.last().unwrap(); @@ -535,18 +548,22 @@ fn resolved_path( } } if w.alternate() { - write!(w, "{}{:#}", &last.name, last.args.print())?; + write!(w, "{}{:#}", &last.name, last.args.print(cache))?; } else { let path = if use_absolute { - if let Some((_, _, fqp)) = href(did) { - format!("{}::{}", fqp[..fqp.len() - 1].join("::"), anchor(did, fqp.last().unwrap())) + if let Some((_, _, fqp)) = href(did, cache) { + format!( + "{}::{}", + fqp[..fqp.len() - 1].join("::"), + anchor(did, fqp.last().unwrap(), cache) + ) } else { last.name.to_string() } } else { - anchor(did, &*last.name.as_str()).to_string() + anchor(did, &*last.name.as_str(), cache).to_string() }; - write!(w, "{}{}", path, last.args.print())?; + write!(w, "{}{}", path, last.args.print(cache))?; } Ok(()) } @@ -555,8 +572,8 @@ fn primitive_link( f: &mut fmt::Formatter<'_>, prim: clean::PrimitiveType, name: &str, + m: &Cache, ) -> fmt::Result { - let m = cache(); let mut needs_termination = false; if !f.alternate() { match m.primitive_locations.get(&prim) { @@ -602,12 +619,15 @@ fn primitive_link( } /// Helper to render type parameters -fn tybounds(param_names: &Option>) -> impl fmt::Display + '_ { +fn tybounds<'a>( + param_names: &'a Option>, + cache: &'a Cache, +) -> impl fmt::Display + 'a { display_fn(move |f| match *param_names { Some(ref params) => { for param in params { write!(f, " + ")?; - fmt::Display::fmt(¶m.print(), f)?; + fmt::Display::fmt(¶m.print(cache), f)?; } Ok(()) } @@ -615,9 +635,9 @@ fn tybounds(param_names: &Option>) -> impl fmt::Display }) } -crate fn anchor(did: DefId, text: &str) -> impl fmt::Display + '_ { +crate fn anchor<'a>(did: DefId, text: &'a str, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| { - if let Some((url, short_ty, fqp)) = href(did) { + if let Some((url, short_ty, fqp)) = href(did, cache) { write!( f, r#"
{}"#, @@ -633,7 +653,14 @@ crate fn anchor(did: DefId, text: &str) -> impl fmt::Display + '_ { }) } -fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) -> fmt::Result { +fn fmt_type( + t: &clean::Type, + f: &mut fmt::Formatter<'_>, + use_absolute: bool, + cache: &Cache, +) -> fmt::Result { + debug!("fmt_type(t = {:?})", t); + match *t { clean::Generic(name) => write!(f, "{}", name), clean::ResolvedPath { did, ref param_names, ref path, is_generic } => { @@ -641,68 +668,69 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) -> f.write_str("dyn ")?; } // Paths like `T::Output` and `Self::Output` should be rendered with all segments. - resolved_path(f, did, path, is_generic, use_absolute)?; - fmt::Display::fmt(&tybounds(param_names), f) + resolved_path(f, did, path, is_generic, use_absolute, cache)?; + fmt::Display::fmt(&tybounds(param_names, cache), f) } clean::Infer => write!(f, "_"), - clean::Primitive(prim) => primitive_link(f, prim, prim.as_str()), + clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), cache), clean::BareFunction(ref decl) => { if f.alternate() { write!( f, - "{}{:#}fn{:#}{:#}", + "{:#}{}{:#}fn{:#}", + decl.print_hrtb_with_space(cache), decl.unsafety.print_with_space(), print_abi_with_space(decl.abi), - decl.print_generic_params(), - decl.decl.print() + decl.decl.print(cache) ) } else { write!( f, - "{}{}", + "{}{}{}", + decl.print_hrtb_with_space(cache), decl.unsafety.print_with_space(), print_abi_with_space(decl.abi) )?; - primitive_link(f, PrimitiveType::Fn, "fn")?; - write!(f, "{}{}", decl.print_generic_params(), decl.decl.print()) + primitive_link(f, PrimitiveType::Fn, "fn", cache)?; + write!(f, "{}", decl.decl.print(cache)) } } clean::Tuple(ref typs) => { match &typs[..] { - &[] => primitive_link(f, PrimitiveType::Unit, "()"), + &[] => primitive_link(f, PrimitiveType::Unit, "()", cache), &[ref one] => { - primitive_link(f, PrimitiveType::Tuple, "(")?; + primitive_link(f, PrimitiveType::Tuple, "(", cache)?; // Carry `f.alternate()` into this display w/o branching manually. - fmt::Display::fmt(&one.print(), f)?; - primitive_link(f, PrimitiveType::Tuple, ",)") + fmt::Display::fmt(&one.print(cache), f)?; + primitive_link(f, PrimitiveType::Tuple, ",)", cache) } many => { - primitive_link(f, PrimitiveType::Tuple, "(")?; + primitive_link(f, PrimitiveType::Tuple, "(", cache)?; for (i, item) in many.iter().enumerate() { if i != 0 { write!(f, ", ")?; } - fmt::Display::fmt(&item.print(), f)?; + fmt::Display::fmt(&item.print(cache), f)?; } - primitive_link(f, PrimitiveType::Tuple, ")") + primitive_link(f, PrimitiveType::Tuple, ")", cache) } } } clean::Slice(ref t) => { - primitive_link(f, PrimitiveType::Slice, "[")?; - fmt::Display::fmt(&t.print(), f)?; - primitive_link(f, PrimitiveType::Slice, "]") + primitive_link(f, PrimitiveType::Slice, "[", cache)?; + fmt::Display::fmt(&t.print(cache), f)?; + primitive_link(f, PrimitiveType::Slice, "]", cache) } clean::Array(ref t, ref n) => { - primitive_link(f, PrimitiveType::Array, "[")?; - fmt::Display::fmt(&t.print(), f)?; + primitive_link(f, PrimitiveType::Array, "[", cache)?; + fmt::Display::fmt(&t.print(cache), f)?; if f.alternate() { - primitive_link(f, PrimitiveType::Array, &format!("; {}]", n)) + primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cache) } else { - primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n))) + primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cache) } } - clean::Never => primitive_link(f, PrimitiveType::Never, "!"), + clean::Never => primitive_link(f, PrimitiveType::Never, "!", cache), clean::RawPointer(m, ref t) => { let m = match m { hir::Mutability::Mut => "mut", @@ -714,19 +742,26 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) -> primitive_link( f, clean::PrimitiveType::RawPointer, - &format!("*{} {:#}", m, t.print()), + &format!("*{} {:#}", m, t.print(cache)), + cache, ) } else { primitive_link( f, clean::PrimitiveType::RawPointer, - &format!("*{} {}", m, t.print()), + &format!("*{} {}", m, t.print(cache)), + cache, ) } } _ => { - primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m))?; - fmt::Display::fmt(&t.print(), f) + primitive_link( + f, + clean::PrimitiveType::RawPointer, + &format!("*{} ", m), + cache, + )?; + fmt::Display::fmt(&t.print(cache), f) } } } @@ -746,13 +781,15 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) -> primitive_link( f, PrimitiveType::Slice, - &format!("{}{}{}[{:#}]", amp, lt, m, bt.print()), + &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cache)), + cache, ) } else { primitive_link( f, PrimitiveType::Slice, - &format!("{}{}{}[{}]", amp, lt, m, bt.print()), + &format!("{}{}{}[{}]", amp, lt, m, bt.print(cache)), + cache, ) } } @@ -761,36 +798,42 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) -> f, PrimitiveType::Slice, &format!("{}{}{}[", amp, lt, m), + cache, )?; if f.alternate() { - write!(f, "{:#}", bt.print())?; + write!(f, "{:#}", bt.print(cache))?; } else { - write!(f, "{}", bt.print())?; + write!(f, "{}", bt.print(cache))?; } - primitive_link(f, PrimitiveType::Slice, "]") + primitive_link(f, PrimitiveType::Slice, "]", cache) } } } clean::ResolvedPath { param_names: Some(ref v), .. } if !v.is_empty() => { write!(f, "{}{}{}(", amp, lt, m)?; - fmt_type(&ty, f, use_absolute)?; + fmt_type(&ty, f, use_absolute, cache)?; write!(f, ")") } clean::Generic(..) => { - primitive_link(f, PrimitiveType::Reference, &format!("{}{}{}", amp, lt, m))?; - fmt_type(&ty, f, use_absolute) + primitive_link( + f, + PrimitiveType::Reference, + &format!("{}{}{}", amp, lt, m), + cache, + )?; + fmt_type(&ty, f, use_absolute, cache) } _ => { write!(f, "{}{}{}", amp, lt, m)?; - fmt_type(&ty, f, use_absolute) + fmt_type(&ty, f, use_absolute, cache) } } } clean::ImplTrait(ref bounds) => { if f.alternate() { - write!(f, "impl {:#}", print_generic_bounds(bounds)) + write!(f, "impl {:#}", print_generic_bounds(bounds, cache)) } else { - write!(f, "impl {}", print_generic_bounds(bounds)) + write!(f, "impl {}", print_generic_bounds(bounds, cache)) } } clean::QPath { ref name, ref self_type, ref trait_ } => { @@ -802,15 +845,15 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) -> }; if f.alternate() { if should_show_cast { - write!(f, "<{:#} as {:#}>::", self_type.print(), trait_.print())? + write!(f, "<{:#} as {:#}>::", self_type.print(cache), trait_.print(cache))? } else { - write!(f, "{:#}::", self_type.print())? + write!(f, "{:#}::", self_type.print(cache))? } } else { if should_show_cast { - write!(f, "<{} as {}>::", self_type.print(), trait_.print())? + write!(f, "<{} as {}>::", self_type.print(cache), trait_.print(cache))? } else { - write!(f, "{}::", self_type.print())? + write!(f, "{}::", self_type.print(cache))? } }; match *trait_ { @@ -825,7 +868,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) -> // everything comes in as a fully resolved QPath (hard to // look at). box clean::ResolvedPath { did, ref param_names, .. } => { - match href(did) { + match href(did, cache) { Some((ref url, _, ref path)) if !f.alternate() => { write!( f, @@ -851,31 +894,36 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) -> } impl clean::Type { - crate fn print(&self) -> impl fmt::Display + '_ { - display_fn(move |f| fmt_type(self, f, false)) + crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { + display_fn(move |f| fmt_type(self, f, false, cache)) } } impl clean::Impl { - crate fn print(&self) -> impl fmt::Display + '_ { - self.print_inner(true, false) + crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + self.print_inner(true, false, cache) } - fn print_inner(&self, link_trait: bool, use_absolute: bool) -> impl fmt::Display + '_ { + fn print_inner<'a>( + &'a self, + link_trait: bool, + use_absolute: bool, + cache: &'a Cache, + ) -> impl fmt::Display + 'a { display_fn(move |f| { if f.alternate() { - write!(f, "impl{:#} ", self.generics.print())?; + write!(f, "impl{:#} ", self.generics.print(cache))?; } else { - write!(f, "impl{} ", self.generics.print())?; + write!(f, "impl{} ", self.generics.print(cache))?; } if let Some(ref ty) = self.trait_ { - if self.polarity == Some(clean::ImplPolarity::Negative) { + if self.negative_polarity { write!(f, "!")?; } if link_trait { - fmt::Display::fmt(&ty.print(), f)?; + fmt::Display::fmt(&ty.print(cache), f)?; } else { match ty { clean::ResolvedPath { @@ -883,7 +931,7 @@ impl clean::Impl { } => { let last = path.segments.last().unwrap(); fmt::Display::fmt(&last.name, f)?; - fmt::Display::fmt(&last.args.print(), f)?; + fmt::Display::fmt(&last.args.print(cache), f)?; } _ => unreachable!(), } @@ -892,36 +940,39 @@ impl clean::Impl { } if let Some(ref ty) = self.blanket_impl { - fmt_type(ty, f, use_absolute)?; + fmt_type(ty, f, use_absolute, cache)?; } else { - fmt_type(&self.for_, f, use_absolute)?; + fmt_type(&self.for_, f, use_absolute, cache)?; } - fmt::Display::fmt( - &WhereClause { gens: &self.generics, indent: 0, end_newline: true }, - f, - )?; + let where_clause = WhereClause { gens: &self.generics, indent: 0, end_newline: true }; + fmt::Display::fmt(&where_clause.print(cache), f)?; Ok(()) }) } } // The difference from above is that trait is not hyperlinked. -crate fn fmt_impl_for_trait_page(i: &clean::Impl, f: &mut Buffer, use_absolute: bool) { - f.from_display(i.print_inner(false, use_absolute)) +crate fn fmt_impl_for_trait_page( + i: &clean::Impl, + f: &mut Buffer, + use_absolute: bool, + cache: &Cache, +) { + f.from_display(i.print_inner(false, use_absolute, cache)) } impl clean::Arguments { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| { for (i, input) in self.values.iter().enumerate() { if !input.name.is_empty() { write!(f, "{}: ", input.name)?; } if f.alternate() { - write!(f, "{:#}", input.type_.print())?; + write!(f, "{:#}", input.type_.print(cache))?; } else { - write!(f, "{}", input.type_.print())?; + write!(f, "{}", input.type_.print(cache))?; } if i + 1 < self.values.len() { write!(f, ", ")?; @@ -933,41 +984,47 @@ impl clean::Arguments { } impl clean::FnRetTy { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| match self { clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()), - clean::Return(ty) if f.alternate() => write!(f, " -> {:#}", ty.print()), - clean::Return(ty) => write!(f, " -> {}", ty.print()), + clean::Return(ty) if f.alternate() => write!(f, " -> {:#}", ty.print(cache)), + clean::Return(ty) => write!(f, " -> {}", ty.print(cache)), clean::DefaultReturn => Ok(()), }) } } impl clean::BareFunctionDecl { - fn print_generic_params(&self) -> impl fmt::Display + '_ { - comma_sep(self.generic_params.iter().map(|g| g.print())) + fn print_hrtb_with_space<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { + display_fn(move |f| { + if !self.generic_params.is_empty() { + write!(f, "for<{}> ", comma_sep(self.generic_params.iter().map(|g| g.print(cache)))) + } else { + Ok(()) + } + }) } } impl clean::FnDecl { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'a>(&'a self, cache: &'a Cache) -> impl fmt::Display + 'a { display_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; if f.alternate() { write!( f, "({args:#}{ellipsis}){arrow:#}", - args = self.inputs.print(), + args = self.inputs.print(cache), ellipsis = ellipsis, - arrow = self.output.print() + arrow = self.output.print(cache) ) } else { write!( f, "({args}{ellipsis}){arrow}", - args = self.inputs.print(), + args = self.inputs.print(cache), ellipsis = ellipsis, - arrow = self.output.print() + arrow = self.output.print(cache) ) } }) @@ -975,7 +1032,7 @@ impl clean::FnDecl { } impl Function<'_> { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { display_fn(move |f| { let &Function { decl, header_len, indent, asyncness } = self; let amp = if f.alternate() { "&" } else { "&" }; @@ -1011,17 +1068,17 @@ impl Function<'_> { } clean::SelfExplicit(ref typ) => { if f.alternate() { - args.push_str(&format!("self: {:#}", typ.print())); + args.push_str(&format!("self: {:#}", typ.print(cache))); } else { - args.push_str(&format!("self: {}", typ.print())); + args.push_str(&format!("self: {}", typ.print(cache))); } - args_plain.push_str(&format!("self: {:#}", typ.print())); + args_plain.push_str(&format!("self: {:#}", typ.print(cache))); } } } else { if i > 0 { args.push_str("
"); - args_plain.push_str(" "); + args_plain.push(' '); } if !input.name.is_empty() { args.push_str(&format!("{}: ", input.name)); @@ -1029,11 +1086,11 @@ impl Function<'_> { } if f.alternate() { - args.push_str(&format!("{:#}", input.type_.print())); + args.push_str(&format!("{:#}", input.type_.print(cache))); } else { - args.push_str(&input.type_.print().to_string()); + args.push_str(&input.type_.print(cache).to_string()); } - args_plain.push_str(&format!("{:#}", input.type_.print())); + args_plain.push_str(&format!("{:#}", input.type_.print(cache))); } if i + 1 < decl.inputs.values.len() { args.push(','); @@ -1054,11 +1111,11 @@ impl Function<'_> { Cow::Borrowed(&decl.output) }; - let arrow_plain = format!("{:#}", &output.print()); + let arrow_plain = format!("{:#}", &output.print(cache)); let arrow = if f.alternate() { - format!("{:#}", &output.print()) + format!("{:#}", &output.print(cache)) } else { - output.print().to_string() + output.print(cache).to_string() }; let declaration_len = header_len + args_plain.len() + arrow_plain.len(); @@ -1089,13 +1146,13 @@ impl clean::Visibility { self, tcx: TyCtxt<'tcx>, item_did: DefId, + cache: &Cache, ) -> impl fmt::Display + 'tcx { use rustc_span::symbol::kw; - display_fn(move |f| match self { - clean::Public => f.write_str("pub "), - clean::Inherited => Ok(()), - + let to_print = match self { + clean::Public => "pub ".to_owned(), + clean::Inherited => String::new(), clean::Visibility::Restricted(vis_did) => { // FIXME(camelid): This may not work correctly if `item_did` is a module. // However, rustdoc currently never displays a module's @@ -1103,38 +1160,41 @@ impl clean::Visibility { let parent_module = find_nearest_parent_module(tcx, item_did); if vis_did.index == CRATE_DEF_INDEX { - write!(f, "pub(crate) ") + "pub(crate) ".to_owned() } else if parent_module == Some(vis_did) { // `pub(in foo)` where `foo` is the parent module // is the same as no visibility modifier - Ok(()) + String::new() } else if parent_module .map(|parent| find_nearest_parent_module(tcx, parent)) .flatten() == Some(vis_did) { - write!(f, "pub(super) ") + "pub(super) ".to_owned() } else { - f.write_str("pub(")?; let path = tcx.def_path(vis_did); debug!("path={:?}", path); let first_name = path.data[0].data.get_opt_name().expect("modules are always named"); + // modified from `resolved_path()` to work with `DefPathData` + let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); + let anchor = anchor(vis_did, &last_name.as_str(), cache).to_string(); + + let mut s = "pub(".to_owned(); if path.data.len() != 1 || (first_name != kw::SelfLower && first_name != kw::Super) { - f.write_str("in ")?; + s.push_str("in "); } - // modified from `resolved_path()` to work with `DefPathData` - let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); for seg in &path.data[..path.data.len() - 1] { - write!(f, "{}::", seg.data.get_opt_name().unwrap())?; + s.push_str(&format!("{}::", seg.data.get_opt_name().unwrap())); } - let path = anchor(vis_did, &last_name.as_str()).to_string(); - write!(f, "{}) ", path) + s.push_str(&format!("{}) ", anchor)); + s } } - }) + }; + display_fn(move |f| f.write_str(&to_print)) } } @@ -1179,20 +1239,20 @@ impl PrintWithSpace for hir::Mutability { } impl clean::Import { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { display_fn(move |f| match self.kind { clean::ImportKind::Simple(name) => { if name == self.source.path.last() { - write!(f, "use {};", self.source.print()) + write!(f, "use {};", self.source.print(cache)) } else { - write!(f, "use {} as {};", self.source.print(), name) + write!(f, "use {} as {};", self.source.print(cache), name) } } clean::ImportKind::Glob => { if self.source.path.segments.is_empty() { write!(f, "use *;") } else { - write!(f, "use {}::*;", self.source.print()) + write!(f, "use {}::*;", self.source.print(cache)) } } }) @@ -1200,16 +1260,16 @@ impl clean::Import { } impl clean::ImportSource { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { display_fn(move |f| match self.did { - Some(did) => resolved_path(f, did, &self.path, true, false), + Some(did) => resolved_path(f, did, &self.path, true, false, cache), _ => { for seg in &self.path.segments[..self.path.segments.len() - 1] { write!(f, "{}::", seg.name)?; } let name = self.path.last_name(); if let hir::def::Res::PrimTy(p) = self.path.res { - primitive_link(f, PrimitiveType::from(p), &*name)?; + primitive_link(f, PrimitiveType::from(p), &*name, cache)?; } else { write!(f, "{}", name)?; } @@ -1220,23 +1280,23 @@ impl clean::ImportSource { } impl clean::TypeBinding { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { display_fn(move |f| { f.write_str(&*self.name.as_str())?; match self.kind { clean::TypeBindingKind::Equality { ref ty } => { if f.alternate() { - write!(f, " = {:#}", ty.print())?; + write!(f, " = {:#}", ty.print(cache))?; } else { - write!(f, " = {}", ty.print())?; + write!(f, " = {}", ty.print(cache))?; } } clean::TypeBindingKind::Constraint { ref bounds } => { if !bounds.is_empty() { if f.alternate() { - write!(f, ": {:#}", print_generic_bounds(bounds))?; + write!(f, ": {:#}", print_generic_bounds(bounds, cache))?; } else { - write!(f, ": {}", print_generic_bounds(bounds))?; + write!(f, ": {}", print_generic_bounds(bounds, cache))?; } } } @@ -1261,26 +1321,26 @@ crate fn print_default_space<'a>(v: bool) -> &'a str { } impl clean::GenericArg { - crate fn print(&self) -> impl fmt::Display + '_ { + crate fn print<'b, 'a: 'b>(&'a self, cache: &'b Cache) -> impl fmt::Display + 'b { display_fn(move |f| match self { clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(<.print(), f), - clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(), f), + clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cache), f), clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(), f), }) } } crate fn display_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Display { - WithFormatter(Cell::new(Some(f))) -} - -struct WithFormatter(Cell>); - -impl fmt::Display for WithFormatter -where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (self.0.take()).unwrap()(f) + struct WithFormatter(Cell>); + + impl fmt::Display for WithFormatter + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (self.0.take()).unwrap()(f) + } } + + WithFormatter(Cell::new(Some(f))) } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index d21998bb8c..7e50d72e60 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -7,7 +7,7 @@ use crate::html::escape::Escape; -use std::fmt::{Display, Write}; +use std::fmt::Display; use std::iter::Peekable; use rustc_lexer::{LiteralKind, TokenKind}; @@ -15,16 +15,18 @@ use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::with_default_session_globals; +use super::format::Buffer; + /// Highlights `src`, returning the HTML output. crate fn render_with_highlighting( - src: String, + src: &str, + out: &mut Buffer, class: Option<&str>, playground_button: Option<&str>, tooltip: Option<(Option, &str)>, edition: Edition, -) -> String { +) { debug!("highlighting: ================\n{}\n==============", src); - let mut out = String::with_capacity(src.len()); if let Some((edition_info, class)) = tooltip { write!( out, @@ -35,23 +37,19 @@ crate fn render_with_highlighting( } else { String::new() }, - ) - .unwrap(); + ); } - write_header(&mut out, class); - write_code(&mut out, &src, edition); - write_footer(&mut out, playground_button); - - out + write_header(out, class); + write_code(out, &src, edition); + write_footer(out, playground_button); } -fn write_header(out: &mut String, class: Option<&str>) { - write!(out, "
\n", class.unwrap_or_default())
-        .unwrap()
+fn write_header(out: &mut Buffer, class: Option<&str>) {
+    write!(out, "
\n", class.unwrap_or_default());
 }
 
-fn write_code(out: &mut String, src: &str, edition: Edition) {
+fn write_code(out: &mut Buffer, src: &str, edition: Edition) {
     // This replace allows to fix how the code source with DOS backline characters is displayed.
     let src = src.replace("\r\n", "\n");
     Classifier::new(&src, edition).highlight(&mut |highlight| {
@@ -63,8 +61,8 @@ fn write_code(out: &mut String, src: &str, edition: Edition) {
     });
 }
 
-fn write_footer(out: &mut String, playground_button: Option<&str>) {
-    write!(out, "
{}
\n", playground_button.unwrap_or_default()).unwrap() +fn write_footer(out: &mut Buffer, playground_button: Option<&str>) { + write!(out, "
{}
\n", playground_button.unwrap_or_default()); } /// How a span of text is classified. Mostly corresponds to token kinds. @@ -331,13 +329,13 @@ impl<'a> Classifier<'a> { /// Called when we start processing a span of text that should be highlighted. /// The `Class` argument specifies how it should be highlighted. -fn enter_span(out: &mut String, klass: Class) { - write!(out, "", klass.as_html()).unwrap() +fn enter_span(out: &mut Buffer, klass: Class) { + write!(out, "", klass.as_html()); } /// Called at the end of a span of highlighted text. -fn exit_span(out: &mut String) { - write!(out, "").unwrap() +fn exit_span(out: &mut Buffer) { + out.write_str(""); } /// Called for a span of text. If the text should be highlighted differently @@ -351,10 +349,10 @@ fn exit_span(out: &mut String) { /// ``` /// The latter can be thought of as a shorthand for the former, which is more /// flexible. -fn string(out: &mut String, text: T, klass: Option) { +fn string(out: &mut Buffer, text: T, klass: Option) { match klass { - None => write!(out, "{}", text).unwrap(), - Some(klass) => write!(out, "{}", klass.as_html(), text).unwrap(), + None => write!(out, "{}", text), + Some(klass) => write!(out, "{}", klass.as_html(), text), } } diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index f97c8a7ab7..305cf61091 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -1,4 +1,5 @@ use super::write_code; +use crate::html::format::Buffer; use expect_test::expect_file; use rustc_span::edition::Edition; @@ -18,9 +19,9 @@ const STYLE: &str = r#" fn test_html_highlighting() { let src = include_str!("fixtures/sample.rs"); let html = { - let mut out = String::new(); + let mut out = Buffer::new(); write_code(&mut out, src, Edition::Edition2018); - format!("{}
{}
\n", STYLE, out) + format!("{}
{}
\n", STYLE, out.into_inner()) }; expect_file!["fixtures/sample.html"].assert_eq(&html); } @@ -30,7 +31,7 @@ fn test_dos_backline() { let src = "pub fn foo() {\r\n\ println!(\"foo\");\r\n\ }\r\n"; - let mut html = String::new(); + let mut html = Buffer::new(); write_code(&mut html, src, Edition::Edition2018); - expect_file!["fixtures/dos_line.html"].assert_eq(&html); + expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner()); } diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index b5169b0599..e5a686bd07 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -1,6 +1,7 @@ -use std::collections::HashMap; use std::path::PathBuf; +use rustc_data_structures::fx::FxHashMap; + use crate::externalfiles::ExternalHtml; use crate::html::escape::Escape; use crate::html::format::{Buffer, Print}; @@ -11,7 +12,7 @@ crate struct Layout { crate logo: String, crate favicon: String, crate external_html: ExternalHtml, - crate default_settings: HashMap, + crate default_settings: FxHashMap, crate krate: String, /// The given user css file which allow to customize the generated /// documentation theme. @@ -111,12 +112,8 @@ crate fn render(
\
\ {after_content}\ - \ +
\ - {static_extra_scripts}\ {extra_scripts}\ \ \ @@ -137,22 +134,23 @@ crate fn render( root_path = page.root_path, css_class = page.css_class, logo = { - let p = format!("{}{}", page.root_path, layout.krate); - let p = ensure_trailing_slash(&p); if layout.logo.is_empty() { format!( - "\ + "\ ", - path = p, + root = page.root_path, + path = ensure_trailing_slash(&layout.krate), static_root_path = static_root_path, suffix = page.resource_suffix ) } else { format!( - "\ -
logo
", - p, layout.logo + "\ +
logo
", + root = page.root_path, + path = ensure_trailing_slash(&layout.krate), + logo = layout.logo ) } }, @@ -196,7 +194,7 @@ crate fn render( )) .collect::(), suffix = page.resource_suffix, - static_extra_scripts = page + extra_scripts = page .static_extra_scripts .iter() .map(|e| { @@ -206,17 +204,13 @@ crate fn render( extra_script = e ) }) - .collect::(), - extra_scripts = page - .extra_scripts - .iter() - .map(|e| { + .chain(page.extra_scripts.iter().map(|e| { format!( "", root_path = page.root_path, extra_script = e ) - }) + })) .collect::(), filter_crates = if layout.generate_search_filter { "